.NET Multi-platform App UI (.NET MAUI) 图形支持传统图形转换,这些转换在 ICanvas 对象上实现为方法。 从数学上讲,转换会在呈现图形对象时更改在 ICanvas 绘图方法中指定的坐标和大小。
支持以下转换:
- 平移,将坐标从一个位置移动到另一个位置。
- 缩放,增加或减少坐标和大小。
- 旋转,围绕点旋转坐标。
- 倾斜,水平或垂直移动坐标。
这些转换称为仿射转换。 仿射转换始终保持并行线,并且永远不会导致坐标或大小变为无穷大小。
.NET MAUI VisualElement 类还支持以下转换属性:TranslationX、TranslationY、Scale、Rotation、RotationX 和 RotationY。 但是,Microsoft.Maui.Graphics 转换和 VisualElement 转换之间存在一些差异:
- Microsoft.Maui.Graphics 转换是方法,而 VisualElement 转换是属性。 因此,Microsoft.Maui.Graphics 转换执行操作,而 VisualElement 转换设置状态。 Microsoft.Maui.Graphics 转换适用于随后绘制的图形对象,但不适用于在应用转换之前绘制的图形对象。 相比之下,一旦设置了属性,VisualElement 转换就会应用于以前呈现的元素。 因此,Microsoft.Maui.Graphics 转换在调用方法时是累积的,而 VisualElement 转换在将属性设置为其他值时被替换。
- Microsoft.Maui.Graphics 转换应用于 ICanvas 对象,而 VisualElement 转换应用于 VisualElement 派生对象。
- Microsoft.Maui.Graphics 转换相对于 ICanvas 的左上角,而 VisualElement 转换相对于应用它们的 VisualElement 的左上角。
平移转换
平移转换在水平和垂直方向上移动图形对象。 可以视平移为不必要操作,因为可以通过更改所使用绘图方法的坐标来获得相同的结果。 但是,在显示路径时,所有坐标都封装在路径中,因此应用平移转换来移动整个路径通常会更容易。
Translate 方法需要类型为 float
的 x
和 y
参数,这些参数会使后续绘制的图形对象水平和垂直移动。 负 x
值将对象移向左侧,而正值将对象移向右侧。 负 y
值向上移动对象,而正值向下移动对象。
平移转换的常见用途是呈现最初使用便于绘制的坐标创建的图形对象。 以下示例为 11 角星创建 PathF 对象:
PathF path = new PathF();
for (int i = 0; i < 11; i++)
{
double angle = 5 * i * 2 * Math.PI / 11;
PointF point = new PointF(100 * (float)Math.Sin(angle), -100 * (float)Math.Cos(angle));
if (i == 0)
path.MoveTo(point);
else
path.LineTo(point);
}
canvas.FillColor = Colors.Red;
canvas.Translate(150, 150);
canvas.FillPath(path);
星星的中心位于 (0,0),星星的点位于围绕该点的圆周上。 每个点都是角度的正弦值和余弦值的组合,该角度增加了 360 度的 5/11。 圆的半径设置为 100。 如果在没有任何转换的情况下显示 PathF 对象,则星形的中心将位于 ICanvas 的左上角,并且只有四分之一可见。 因此,使用平移转换将星形水平和垂直移动到 (150,150):
缩放转换
缩放转换会更改图形对象的大小,并且还经常会在图形对象变大时导致坐标移动。
Scale 方法需要类型为 float
的 x
和 y
参数,以便允许为水平和垂直缩放指定不同的值,也称为各向异性缩放。 x
和 y
的值对生成的缩放有重大影响:
- 介于 0 和 1 之间的值会减小缩放对象的宽度和高度。
- 大于 1 的值会增加缩放对象的宽度和高度。
- 值等于 1 指示对象未缩放。
以下示例演示了 Scale 方法:
canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 4;
canvas.StrokeDashPattern = new float[] { 2, 2 };
canvas.FontColor = Colors.Blue;
canvas.FontSize = 18;
canvas.DrawRoundedRectangle(50, 50, 80, 20, 5);
canvas.DrawString(".NET MAUI", 50, 50, 80, 20, HorizontalAlignment.Left, VerticalAlignment.Top);
canvas.Scale(2, 2);
canvas.DrawRoundedRectangle(50, 100, 80, 20, 5);
canvas.DrawString(".NET MAUI", 50, 100, 80, 20, HorizontalAlignment.Left, VerticalAlignment.Top);
在此示例中,“.NET MAUI”显示在用虚线描边的圆角矩形内。 在 Scale 调用后绘制的相同图形对象其大小将按比例增加:
文本和圆角矩形都受相同缩放系数的约束。
注意
各向异性缩放会导致与水平轴和垂直轴对齐的线条的描边大小所有不同。
组合 Translate 和 Scale 调用时,顺序很重要。 如果 Translate 调用发生在 Scale 调用之后,则平移因子将按缩放因子进行缩放。 如果 Translate 调用在 Scale 调用之前,则不缩放平移因子。
旋转转换
旋转转换可围绕一个点旋转图形对象。 旋转为顺时针方向时会增加角度。 允许出现负角和大于 360 度的角度。
存在两个 Rotate 重载。 第一个需要 float
类型的 degrees
参数,该参数定义旋转角度,并使旋转以画布的左上角 (0,0) 为中心。 下面的示例演示了此 Rotate 方法:
canvas.FontColor = Colors.Blue;
canvas.FontSize = 18;
canvas.Rotate(45);
canvas.DrawString(".NET MAUI", 50, 50, HorizontalAlignment.Left);
在此示例中,“.NET MAUI”顺时针旋转 45 度:
或者,图形对象可以以特定点为中心旋转。 若要围绕特定点旋转,请使用接受 float
类型的 degrees
、x
和 y
参数的 Rotate 重载:
canvas.FontColor = Colors.Blue;
canvas.FontSize = 18;
canvas.Rotate(45, dirtyRect.Center.X, dirtyRect.Center.Y);
canvas.DrawString(".NET MAUI", dirtyRect.Center.X, dirtyRect.Center.Y, HorizontalAlignment.Left);
在此示例中,.NET MAUI 文本围绕画布中心旋转 45 度。
组合转换
组合转换的最简单方法是从全局转换开始,然后是局部转换。 例如,可以组合平移、缩放和旋转来绘制模拟时钟。 可以使用任意坐标系绘制时钟,该坐标系基于以 (0,0) 为圆心、半径为 100 的圆。 平移和缩放在画布上展开时钟并使其居中,然后可以使用旋转来绘制时钟的分钟和小时标记并旋转指针:
canvas.StrokeLineCap = LineCap.Round;
canvas.FillColor = Colors.Gray;
// Translation and scaling
canvas.Translate(dirtyRect.Center.X, dirtyRect.Center.Y);
float scale = Math.Min(dirtyRect.Width / 200f, dirtyRect.Height / 200f);
canvas.Scale(scale, scale);
// Hour and minute marks
for (int angle = 0; angle < 360; angle += 6)
{
canvas.FillCircle(0, -90, angle % 30 == 0 ? 4 : 2);
canvas.Rotate(6);
}
DateTime now = DateTime.Now;
// Hour hand
canvas.StrokeSize = 20;
canvas.SaveState();
canvas.Rotate(30 * now.Hour + now.Minute / 2f);
canvas.DrawLine(0, 0, 0, -50);
canvas.RestoreState();
// Minute hand
canvas.StrokeSize = 10;
canvas.SaveState();
canvas.Rotate(6 * now.Minute + now.Second / 10f);
canvas.DrawLine(0, 0, 0, -70);
canvas.RestoreState();
// Second hand
canvas.StrokeSize = 2;
canvas.SaveState();
canvas.Rotate(6 * now.Second);
canvas.DrawLine(0, 10, 0, -80);
canvas.RestoreState();
在此示例中,Translate 和 Scale 调用全局应用于时钟,因此在 Rotate 方法之前调用。
存在 60 个两种不同大小的标记,它们围绕时钟绘制成一个圆圈。 FillCircle 调用在 (0,-90) 处画圆圈,该圆圈相对于时钟的中心对应于 12:00。 Rotate 调用在每个对勾标记后将旋转角度增加 6 度。 angle
变量仅用于确定绘制的是大圆圈还是小圆圈。 最后,将获得当前时间,并计算时针、分针和秒针的旋转角度。 在 12:00 位置绘制每个指针,以便旋转角度相对于该位置:
连接转换
转换可以根据 3×3 仿射转换矩阵来描述,其在 2D 空间中执行转换。 下表显示了 3x3 仿射转换矩阵的结构:
M11
M12
0.0
M21
M22
0.0
M31
M32
1.0
仿射转换矩阵的最后一列等于 (0,0,1),因此只需指定前两列中的成员。 因此,3x3 矩阵由 System.Numerics 命名空间中的 Matrix3x2
结构表示,该结构是三行两列 float
值的集合。
转换矩阵的前两列中的六个单元格表示执行缩放、剪切和平移的值:
ScaleX
ShearY
0.0
ShearX
ScaleY
0.0
TranslateX
TranslateY
1.0
例如,如果将 M31
值更改为 100,则可以使用它将图形对象沿 x 轴平移 100 个像素。 如果将 M22
值更改为 3,则可以使用它将图形对象拉伸到其当前高度的三倍。 如果同时更改这两个值,将使图形对象沿 x 轴移动 100 像素,并将其高度拉伸 3 倍。
可以使用 Matrix3x2
构造函数定义新的转换矩阵。 使用转换矩阵指定转换的优点是可将复合转换应用为单个转换,这称为串联。 Matrix3x2
结构还定义了可用于操作矩阵值的方法。
只有接受 Matrix3x2
参数的 ICanvas 方法是 ConcatenateTransform 方法,可将多个转换组合为单个转换。 以下示例演示如何使用此方法转换 PathF 对象:
PathF path = new PathF();
for (int i = 0; i < 11; i++)
{
double angle = 5 * i * 2 * Math.PI / 11;
PointF point = new PointF(100 * (float)Math.Sin(angle), -100 * (float)Math.Cos(angle));
if (i == 0)
path.MoveTo(point);
else
path.LineTo(point);
}
Matrix3x2 transform = new Matrix3x2(1.5f, 1, 0, 1, 150, 150);
canvas.ConcatenateTransform(transform);
canvas.FillColor = Colors.Red;
canvas.FillPath(path);
在此示例中,PathF 对象在 x 轴上缩放和剪切,并在 x 轴和 y 轴上转换。