高级Flutter: 矩阵(Matrix4)与透视变换(译文)
揭开 matrix4的神秘面纱,并充分利用转换小部件的能力
本文首次发表在codemagic.io!
Codemagic为Flutter和移动应用程序项目提供持续集成和持续交付。Developed by Nevercode。
Transform 组件是 Flutter 目录中最强大的小部件之一(对我来说,也是最被低估的)。Transform 允许我们从根本上改变组件的外观和行为,允许我们创建新的、复杂的动画类型。在 Transform 组件下面,在 Transform 组件下,一个4D矩阵为实际的转换提供动力——由 Matrix4 类定义。虽然 Flutter 已经提供了一些简单的转换方法,比如平移、缩放和旋转,但是我们可以使用 Matrix4 来创建更棒的东西,比如3d 透视转换。
在这篇文章中,我们将探索4D矩阵本身以及其中的个体值是做什么的。在我之前写的 Transform Deep Dive 中,我们讨论了如何在不直接与4D矩阵交互的情况下使用 Transform。如果你需要复习一下这个小部件的简单使用方法,可以查看一下。
什么是4D 矩阵?
尽管任何带有“4D”的东西在默认情况下听起来都很酷,但实际上,4D 矩阵只是一个4行4列的矩阵。我们需要使用一个4d 矩阵来转换一个物体的三维(在这里,维度是我们习惯的:长度、宽度和高度)。
这种矩阵的形成称为单位矩阵。想象一个单位矩阵的最佳方式是,它等价于矩阵形式中的数字“1”——用于转换窗口小部件时,它不会改变任何东西。
在这个矩阵中使用不同的数字组合,我们可以操纵给定对象的形状、大小、方向等。
让我们来看看我们是如何做到的。
基本设置
让我们来看看我们将要用于实验的代码:
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double x = 0;
double y = 0;
double z = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Transform(
transform: Matrix4(
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1,
)..rotateX(x)..rotateY(y)..rotateZ(z),
alignment: FractionalOffset.center,
child: GestureDetector(
onPanUpdate: (details) {
setState(() {
y = y - details.delta.dx / 100;
x = x + details.delta.dy / 100;
});
},
child: Container(
color: Colors.red,
height: 200.0,
width: 200.0,
),
),
),
),
);
}
}
代码只是使用 Transform 组件和彩色 Container 进行转换。我们为起点定义了一个恒等矩阵,它确实……嗯……什么作用都没有。使用 Gestuedector 的部分允许我们以 x 和 y 的方向旋转正方形。如果你需要更多关于我们正在做什么的信息,我建议你看看 Wm Leler 的文章《Flutter透视》,其中一个类似的方法是使用x、 y 和 z 变量来跟踪已经完成的旋转量。
稍后我们将回到为什么我们允许用户旋转正方形的问题。现在,我们专注于矩阵和基本的2D结果。
*注一:
**alignment: FractionalOffset.center**
将转换的中心点设置为正方形的中心。注 2(对于书呆子):Matrix4 默认以列优先顺序排列。 在编写代码的方式上,我们有效地以行优先格式编写它。 因此,写入的所有行和列值都将沿对角线反转。
下面是现在屏幕的样子:
我们在那里有一个非常有趣的方形。 让我们看看我们是否可以用它做点什么。
使用矩阵进行缩放
让我们尝试在 x、y、z 方向上单独缩放(扩展/收缩)这个正方形,然后再一起缩放。
X轴的缩放
若要在 x 方向(水平方向)缩放一个对象,请根据所需的缩放因子更改矩阵的(0, 0)值。
让我们试着这样做,当比例因子为1.5时,我们的正方形变成:
transform: Matrix4(
1.5, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
)..rotateX(x)..rotateY(y)..rotateZ(z),
Y轴的缩放
若要沿 y 方向(垂直)缩放对象,请根据所需的缩放因子更改矩阵的(0, 0)值。
让我们试着这样做,当比例因子为1.5时,我们的正方形变成:
transform: Matrix4(
1, 0, 0, 0,
0, 1.5, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
)..rotateX(x)..rotateY(y)..rotateZ(z),
在z轴中缩放呢?
理论上,该值(2, 2)将沿 z 方向扩展,并具有类似的矩阵。
因为这并不适用于我们的2D方形,我们就让它过吧。
向各个方向扩展
一种向所有方向缩放的方法是将上述方法结合起来,并使用它来均匀缩放我们的正方形。然而,我们也可以使用另一种方法:
我们可以用1除以我们想要使用的比例因子来代替位置(3, 3)。所以如果我们想让这个正方形增大两倍,我们会用1/2,也就是0.5。
下面是使用在(3, 3)处使用0.5的正方形:
transform: Matrix4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1.5,
)..rotateX(x)..rotateY(y)..rotateZ(z),
使用矩阵进行转换
为了在 x、y 或 z 方向中转换,我们可以很容易地改变底部 x、y 和 z 三个的值。
重要提示: 上面给出的矩阵表示代码中的矩阵。由于实际的 matrix4采用列主格式,因此 x、 y 和 z 分别为(0, 3)、(1, 3)和(2, 3)。
让我们以 x 的值等于75为例:
transform: Matrix4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
75, 0, 0, 1,
)..rotateX(x)..rotateY(y)..rotateZ(z),
使用矩阵旋转
生活中所有的事情都不容易——因此,就像用矩阵缩放和平移物体一样简单,旋转也不是一件容易记住的事情,而且你可能会在数学期末考试中搞砸。
围绕 x 轴旋转:
围绕 y 轴旋转:
围绕 z 轴旋转:
这可能正好解释了为什么在开始的代码中我们有:
..rotateX(x)..rotateY(y)..rotateZ(z)
而不是手动实现角度和进一步的矩阵乘法。
透视变换
透视变换修改轴上位置的长度。最常见的用途是给用户一种深度感ーー就像你站的地方铁轨看起来很宽,但是当你看得更远时,它们就会变得越来越短。在这种情况下,靠近我们的那一边应该看起来更大,而远离我们的那一边应该看起来更小。
当正方形旋转时,没有深度感。我们可以使用透视变换来解决这个问题。要启用 z 方向的深度知觉,我们需要在代码中的矩阵中设置这个值。
你可能已经看到其他教程使用这一行:
..setEntry(3, 2, 0.001)
当物体远离时,它们的长度减少,当物体靠近时,它们的长度增加。
让我们把 z 值设置为0.002,看看我们的正方形会发生什么。
double z = 0.002;
我们现在可以看到差异,近的一面变大,远的一面变小。较高的值会给出较高的长度梯度的距离。
但是... 不是这样的
如果您注意到本文中所有轴的对称主题,则可能会思考“Z轴的深度感知值的两个值呢”?
与 z 轴类似,其他两个值也以相同的方式工作。
设置这个值可以让我们感知沿着 x 轴的距离,就像我们感知 z 轴一样。虽然这不一定是相同的效果,但这在理论上仍然与上面所做的透视图转换相同。
在这里,当 x 轴的位置增加时,边的长度减少,大概是这样:
类似地,y 轴:
计算机图形学中的原点在左上角,因此,Y的位置随着向下移动而增加。因此,长度会随着Y值的减小而减小。