倾斜转换
查看在 SkiaSharp 中倾斜转换如何创建倾斜图形对象
在 SkiaSharp 中,倾斜转换会使图形对象倾斜,例如此图像中的阴影:
倾斜会将矩形转换为平行四边形,但倾斜后椭圆形仍是椭圆形。
尽管 Xamarin.Forms 会定义转换、缩放和旋转的属性,但 Xamarin.Forms 中没有对应于倾斜的属性。
SKCanvas
的 Skew
方法接受水平倾斜和垂直倾斜的两个参数:
public void Skew (Single xSkew, Single ySkew)
第二个 Skew
方法将这些参数组合在单个 SKPoint
值中:
public void Skew (SKPoint skew)
但是,不太可能单独使用这两种方法之一。
“倾斜试验”页允许你试验介于 -10 和 10 之间的倾斜值。 文本字符串位于页面左上角,并具有从两个 Slider
元素获取的倾斜值。 下面是 SkewExperimentPage
类中的 PaintSurface
处理程序:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Blue,
TextSize = 200
})
{
string text = "SKEW";
SKRect textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);
canvas.Skew((float)xSkewSlider.Value, (float)ySkewSlider.Value);
canvas.DrawText(text, 0, -textBounds.Top, textPaint);
}
}
xSkew
参数的正数值会使文本底部向右移动,其负数值会使文本底部向左移动。 ySkew
的正数值会使文本右侧向下移动,其负数值会使文本右侧向上移动:
如果 xSkew
值是 ySkew
值的负值,则结果为旋转,但也会有所缩放。
转换公式如下所示:
x' = x + xSkew · y
y' = ySkew · x + y
例如,对于正数 xSkew
值,转换后的 x'
值会随着 y
的增加而增加。 这就是导致倾斜的原因。
如果三角形宽为 200 像素,高度为 100 像素,其左上角位于点 (0, 0),并且以 1.5 的 xSkew
值呈现,则会得到以下平行四边形:
底边的坐标具有 100 的 y
值,因此向右移动了 150 像素。
对于 xSkew
或 ySkew
的非零值,只有点 (0, 0) 保持不变。 该点可以被视为是倾斜的中心。 如果需要倾斜中心作为其他内容(通常会是这种情况),则没有 Skew
方法会提供该功能。 需要将 Translate
调用与 Skew
调用显式合并。 若要将倾斜居中于 px
和 py
,请进行以下调用:
canvas.Translate(px, py);
canvas.Skew(xSkew, ySkew);
canvas.Translate(-px, -py);
复合转换公式为:
x' = x + xSkew · (y – py)
y' = ySkew · (x – px) + y
如果 ySkew
为零,则不会使用 px
值。 该值与 ySkew
和 py
类似,是不相关的。
你可能会觉得用倾斜角来表示倾斜更合适,例如本图中的角度 α:
150 像素移动量与 100 像素垂直量的比值就是该角度的正切值,在本例中为 56.3 度。
“倾斜角度试验”页的 XAML 文件类似于“倾斜角度”页,但 Slider
元素的范围从 –90 度到 90 度不等。 SkewAngleExperiment
代码隐藏文件使页面上的文本居中,并使用 Translate
将倾斜中心设置为页面中心。 代码底部的短 SkewDegrees
方法会将角度转换为倾斜值:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Blue,
TextSize = 200
})
{
float xCenter = info.Width / 2;
float yCenter = info.Height / 2;
string text = "SKEW";
SKRect textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);
float xText = xCenter - textBounds.MidX;
float yText = yCenter - textBounds.MidY;
canvas.Translate(xCenter, yCenter);
SkewDegrees(canvas, xSkewSlider.Value, ySkewSlider.Value);
canvas.Translate(-xCenter, -yCenter);
canvas.DrawText(text, xText, yText, textPaint);
}
}
void SkewDegrees(SKCanvas canvas, double xDegrees, double yDegrees)
{
canvas.Skew((float)Math.Tan(Math.PI * xDegrees / 180),
(float)Math.Tan(Math.PI * yDegrees / 180));
}
当角度接近正 90 度或负 90 度时,正切值会接近无穷大,但最多 80 度左右的角度是可用的:
如 "斜体文字"页面所示,小幅负水平倾斜可模仿斜体或斜体文字。 ObliqueTextPage
类显示其完成方式:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint textPaint = new SKPaint()
{
Style = SKPaintStyle.Fill,
Color = SKColors.Maroon,
TextAlign = SKTextAlign.Center,
TextSize = info.Width / 8 // empirically determined
})
{
canvas.Translate(info.Width / 2, info.Height / 2);
SkewDegrees(canvas, -20, 0);
canvas.DrawText(Title, 0, 0, textPaint);
}
}
void SkewDegrees(SKCanvas canvas, double xDegrees, double yDegrees)
{
canvas.Skew((float)Math.Tan(Math.PI * xDegrees / 180),
(float)Math.Tan(Math.PI * yDegrees / 180));
}
SKPaint
的 TextAlign
属性设置为 Center
。 如果没有任何转换,带 (0, 0) 坐标的 DrawText
调用会将文本定位在左上角基线的水平中心。 SkewDegrees
会将文本相对于基线水平倾斜 20 度。 Translate
调用会将文本基线的水平中心移动到画布中心:
倾斜阴影文本页面演示了如何使用 45 度倾斜和垂直比例的组合来制作偏离文字的文字阴影。 下面是 PaintSurface
处理程序的相关部分:
using (SKPaint textPaint = new SKPaint())
{
textPaint.Style = SKPaintStyle.Fill;
textPaint.TextSize = info.Width / 6; // empirically determined
// Common to shadow and text
string text = "Shadow";
float xText = 20;
float yText = info.Height / 2;
// Shadow
textPaint.Color = SKColors.LightGray;
canvas.Save();
canvas.Translate(xText, yText);
canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
canvas.Scale(1, 3);
canvas.Translate(-xText, -yText);
canvas.DrawText(text, xText, yText, textPaint);
canvas.Restore();
// Text
textPaint.Color = SKColors.Blue;
canvas.DrawText(text, xText, yText, textPaint);
}
首先显示阴影,然后显示文本:
传递给 DrawText
方法的垂直坐标指示文本相对于基线的位置。 这与用于倾斜中心的垂直坐标相同。 如果文本字符串包含下行字符,此方法将不起作用。 例如,将单词“quirky”替换为“Shadow”,结果如下:
阴影和文本仍与基线对齐,但效果看起来不太对。 若要修复此问题,需要获取文本边界:
SKRect textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);
需要根据下行字母的高度进行调整 Translate
调用:
canvas.Translate(xText, yText + textBounds.Bottom);
canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
canvas.Scale(1, 3);
canvas.Translate(-xText, -yText - textBounds.Bottom);
现在,阴影从这些下行字符的底部延伸: