倾斜转换The Skew Transform

下载示例下载示例Download Sample Download the sample

请参阅如何倾斜转换可以在 SkiaSharp 中创建倾斜的图形对象See how the skew transform can create tilted graphical objects in SkiaSharp

SkiaSharp 中, 倾斜转换将倾斜图形对象,如在此图中的阴影:In SkiaSharp, the skew transform tilts graphical objects, such as the shadow in this image:

倾斜将转换为一个平行四边形的矩形,但偏斜的椭圆仍是一个椭圆。The skew turns a rectangle into a parallelogram, but a skewed ellipse is still an ellipse.

虽然 Xamarin.Forms 定义平移、 缩放和旋转的属性,但没有对应的属性中 Xamarin.Forms 偏差。Although Xamarin.Forms defines properties for translation, scaling, and rotations, there is no corresponding property in Xamarin.Forms for skew.

Skew 方法的SKCanvas接受倾斜的水平扭曲和垂直两个参数:The Skew method of SKCanvas accepts two arguments for horizontal skew and vertical skew:

public void Skew (Single xSkew, Single ySkew)

第二个 Skew 方法结合了这些参数在单个SKPoint值:A second Skew method combines those arguments in a single SKPoint value:

public void Skew (SKPoint skew)

但是,不太可能,您将这两种方法之一中使用隔离。However, it's unlikely that you'll be using either of these two methods in isolation.

倾斜试验页可让您体验偏差值的 – 10 和 10 之间的范围。The Skew Experiment page lets you experiment with skew values that range between –10 and 10. 使用来自两个偏差值在页上,在窗口左上角中定位文本字符串Slider元素。A text string is positioned in the upper-left corner of the page, with skew values obtained from two Slider elements. 下面是PaintSurface处理程序中的 SkewExperimentPage 类:Here is the PaintSurface handler in the SkewExperimentPage class:

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参数转变底部的文本适合正值或负值的左侧。Values of the xSkew argument shift the bottom of the text right for positive values or left for negative values. ySkew对于正值或负值进行向下移动文本右侧:Values of ySkew shift the right of the text down for positive values or up for negative values:

如果xSkew值为的ySkew值,结果是旋转,但还应为某种程度上进行缩放 UWP 显示指示。If the xSkew value is the negative of the ySkew value, the result is rotation, but also scaled somewhat as the UWP display indicates.

转换公式如下所示:The transform formulas are as follows:

x = x + xSkew 推荐配置yx' = x + xSkew · y

y = ySkew 推荐配置x + yy' = ySkew · x + y

例如,对于一个正xSkew值,已转换x'值随着y会增加。For example, for a positive xSkew value, the transformed x' value increases as y increases. 这就是哪些因素会导致倾斜。That's what causes the tilt.

如果三角形 200 像素宽和高 100 像素定位时,点 (0,0) 在其左上角和使用呈现xSkew值为 1.5,以下的平行四边形结果:If a triangle 200 pixels wide and 100 pixels high is positioned with its upper-left corner at the point (0, 0) and is rendered with an xSkew value of 1.5, the following parallelogram results:

下边缘坐标的y值为 100,因此它是向右移动 150 像素。The coordinates of the bottom edge have y values of 100, so it is shifted 150 pixels to the right.

有关的值为非零xSkewySkew、 仅点 (0,0) 保持不变。For non-zero values of xSkew or ySkew, only the point (0, 0) remains the same. 该点可被视为倾斜的中心。That point can be considered the center of skewing. 如果需要中心的倾斜为其他内容 (这通常都是如此),则没有Skew提供,它的方法。If you need the center of skewing to be something else (which is usually the case), there is no Skew method that provides that. 你将需要进行显式结合Translate使用调用Skew调用。You'll need to explicitly combine Translate calls with the Skew call. 居中在倾斜pxpy,进行以下调用:To center the skewing at px and py, make the following calls:

canvas.Translate(px, py);
canvas.Skew(xSkew, ySkew);
canvas.Translate(-px, -py);

复合转换公式是:The composite transform formulas are:

x = x + xSkew 推荐配置(y – 上一年度)x' = x + xSkew · (y – py)

y = ySkew 推荐配置(x – px) + yy' = ySkew · (x – px) + y

如果ySkew为零,则px不使用值。If ySkew is zero, then the px value is not used. 值是不相关的以及同样ySkewpyThe value is irrelevant, and similarly for ySkew and py.

您可能更喜欢指定倾斜的角度的倾斜,如在此图中的角度 α 作为:You might feel more comfortable specifying skew as an angle of tilt, such as the angle α in this diagram:

为 100 像素垂直 150 像素 shift 的比率为在此示例中该角度的正切值 56.3 度。The ratio of the 150-pixel shift to the 100-pixel vertical is the tangent of that angle, in this example 56.3 degrees.

XAML 文件倾斜角度实验页是类似于倾斜角度页上,不同之处在于Slider元素范围 – 90 度到 90 度。The XAML file of the Skew Angle Experiment page is similar to the Skew Angle page except that the Slider elements range from –90 degrees to 90 degrees. SkewAngleExperiment 代码隐藏文件中心页上的文本,并使用Translate设置中心的倾斜到页面的中心。The SkewAngleExperiment code-behind file centers the text on the page and uses Translate to set a center of skewing to the center of the page. 一小段SkewDegrees方法底部的代码将转换为角度,若要倾斜值:A short SkewDegrees method at the bottom of the code converts angles to skew values:

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 度,正切值接近无穷大,但最多约 80 度或操作的角度可使用:As an angle approaches positive or negative 90 degrees, the tangent approaches infinity, but angles up to about 80 degrees or so are usable:

较小的负水平扭曲可模拟倾斜或斜体文本作为倾斜文本页说明。A small negative horizontal skew can mimic oblique or italic text, as the Oblique Text page demonstrates. ObliqueTextPage 类显示了如何执行操作:The ObliqueTextPage class shows how it's done:

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));
}

TextAlign的属性SKPaint设置为CenterThe TextAlign property of SKPaint is set to Center. 不涉及任何转换,DrawText调用使用的坐标 (0,0) 将放置在左上角的文本的水平居中的基线。Without any transforms, the DrawText call with coordinates of (0, 0) would position the text with the horizontal center of the baseline at the upper-left corner. SkewDegrees相对于基线的 20 度水平扭曲文本。The SkewDegrees skews the text horizontally 20 degrees relative to the baseline. Translate调用将水平居中的文本的基线移到画布的中心:The Translate call moves the horizontal center of the text's baseline to the center of the canvas:

倾斜阴影文字页演示了如何使用组合的 45 度倾斜和垂直缩放以便使其远离文字将倾斜文本阴影。The Skew Shadow Text page demonstrates how to use a combination of a 45-degree skew and vertical scale to make a text shadow that tilts away from the text. 以下是相关的部分PaintSurface处理程序:Here's the pertinent part of the PaintSurface handler:

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);
}

阴影是显示第一个,然后选择文本:The shadow is displayed first and then the text:

垂直坐标传递到DrawText方法指示的位置相对于基线的文本。The vertical coordinate passed to the DrawText method indicates the position of the text relative to the baseline. 这是用于倾斜的中心的相同垂直坐标。That is the same vertical coordinate used for the center of skewing. 如果文本字符串包含下行字母,此方法将无法工作。This technique will not work if the text string contains descenders. 例如,将"古怪"一词替换为"影子",下面是结果:For example, substitute the word "quirky" for "Shadow" and here's the result:

卷影和文本仍对齐的基线,但效果只是看起来有错误。The shadow and text are still aligned at the baseline, but the effect just looks wrong. 若要修复此错误,您需要获取文本边界:To fix it, you need to obtain the text bounds:

SKRect textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);

Translate调用需要进行调整的下行字母的高度:The Translate calls need to be adjusted by the height of the descenders:

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);

现在阴影扩展从这些下行字母的底部:Now the shadow extends from the bottom of those descenders: