正如您所相信的那样,转换并不适用于画布轴,而是实际适用于点。HTML5 2D 渲染上下文的转换由转换矩阵控制。当前绘制状态的变换矩阵在创建当前默认路径以及绘制形状、图像和Path
对象时应用。
这是转换矩阵的样子:
[a c tx]
[b d ty]
[0 0 1]
在哪里,
a - x 比例,
c - x 倾斜,
tx - x 平移
b - y 倾斜,
d - y 比例,
ty - y 平移
假设您尝试在该点绘制一个像素(x, y)
。以下是如何将转换应用于它:
[sx] [a c tx] [x]
[sy] = [b d ty]*[y]
[ 1] [0 0 1] [1]
=>
[sx] [a*x + c*y + tx]
[sy] = [b*x + d*y + ty]
[ 1] [ 0 + 0 + 1]
(sx, sy)
是实际绘制像素的输出位图上的坐标。对于单位矩阵 ( a = 1, c = 0, tx = 0, b = 0, d = 1, ty = 0
),(sx, sy)
将与 相同(x, y)
,这是默认情况。
现在,画布的变换矩阵使用列向量作为坐标这一事实为我们提供了变换似乎以相反顺序应用的原因。
让我们举个例子。您可以使用以下语句更改转换矩阵:
translate(-5, -5);
scale(2, 2);
translate(5, 5);
让我们来看看这些语句如何影响转换矩阵:
//default
[1 0 0]
[0 1 0]
[0 0 1]
->translate(-5, -5)
[1 0 0] [1 0 -5]
[0 1 0] * [0 1 -5]
[0 0 1] [0 0 1]
=>
[1 0 -5]
[0 1 -5] //Using MI = IM = M, I is Identity Matrix
[0 0 1]
->scale(2, 2)
[1 0 -5] [2 0 0]
[0 1 -5] * [0 2 0]
[0 0 1] [0 0 1]
=>
[(1*2 + 0*0 + -5*0) (1*0 + 0*2 + -5*0) (1*0 + 0*0 + -5*1)]
[(0*2 + 1*0 + -5*0) (0*0 + 1*2 + -5*0) (0*0 + 1*0 + -5*1)]
[(0*2 + 0*0 + 1*0) (0*0 + 0*2 + 1*0) (0*0 + 0*0 + 1*1)]
=>
[2 0 -5]
[0 2 -5]
[0 0 1]
->translate(5, 5)
[2 0 -5] [1 0 5]
[0 2 -5] * [0 1 5]
[0 0 1] [0 0 1]
=>
[(2*1 + 0*0 + -5*0) (2*0 + 0*1 + -5*0) (2*5 + 0*5 + -5*1)]
[(0*1 + 2*0 + -5*0) (0*0 + 2*1 + -5*0) (0*5 + 2*5 + -5*1)]
[(0*1 + 0*0 + 1*0) (0*0 + 0*1 + 1*0) (0*5 + 0*5 + 1*1)]
=>
[2 0 5]
[0 2 5]
[0 0 1]
这意味着,对于一个点(x, y)
,对应(sx, sy)
的是
[sx] [2*x + 5] [2*x + 0*y + 5]
[sy] = [2*y + 5] = [0*x + 2*y + 5]
[ 1] [ 1 ] [0*x + 0*y + 1]
因此,如果(5, 5)
是您不希望缩放影响的中心,则使用当前代码将在(15, 15)
.
请注意,我首先将所有三个矩阵相乘以创建新的转换矩阵,这可能无法很清楚为什么转换似乎是反向应用的。但是,如果T1表示第一个 translate 语句,S表示 scale 语句,T2表示第二个 translate 语句,而X表示要转换的点的列向量,很容易看出
T = I*T1*S*T2
=>
T = T1*S*T2
那么,如果C表示 的列向量(sx, sy)
,
C = T*X
=>
C = T1*S*T2*X
=>
C = T1*S*X' //X' is X transformed by T2
=>
C = T1*X'' //X'' is X' transformed by S
=>
C = X''' //X''' is X'' transformed by T1
or
[(x+5)*2 - 5]
C = [(y+5)*2 - 5] = X'''
[ 1 ]