2

所以我知道这里已经有关于它的线索,比如那个。我遵循了上面线程中提出的想法,并且它有效。但是,我不明白它为什么起作用。

这是一个例子:

假设我有一个以 (100, 100) 为中心的正方形,它的宽度/高度为 100。所以它的左上角将位于 (50, 50)。

现在假设我要将 X2 放大到正方形的中心,即放大到 (100, 100)。所以我将编写以下转换序列:

translate(100, 100);
scale(2, 2);
translate(-100, -100);

所以因为画布以相反的顺序应用变换,我变换的正方形的左上角现在将位于 (0, 0),它的高度/宽度将为 200。

好的,假设现在我想将 X2 放大到已经转换的正方形的右下角。所以直观地说,我想执行以下转换序列:

translate(200, 200);
scale(2, 2);
translate(-200, -200);

但它不会起作用,因为画布再次以相反的顺序应用变换。也就是说,如果我总结我的两个转换序列,我会得到:

// First Sequence
translate(100, 100);
scale(2, 2);
translate(-100, -100);

// Second Sequence
translate(200, 200);
scale(2, 2);
translate(-200, -200);

这意味着第二个序列将应用于第一个序列之前的每个点(因为画布将应用从下到上的转换),这是错误的。因此,上面链接中的线程建议如下:

因为序列 2 将首先应用,所以我应该将点 (200, 200) 转换为其原始坐标,方法是对其应用第一个序列的倒数。也就是说,如果T1是表示第一个序列的矩阵,那么它会是这样的:

// First Sequence
translate(100, 100);
scale(2, 2);
translate(-100, -100);

// Second Sequence
var point = SVGPoint(200, 200);
var transformedPoint = point.matrixTransform(T1.inverse());
translate(-transformedPoint.x, -transformedPoint.y);
scale(2, 2);
translate(transformedPoint.x, transformedPoint.y);

但为什么它有效?我真的不明白为什么它应该这样工作......有人可以详细说明吗?

谢谢!

4

2 回答 2

3

HTML5 画布转换发生自上而下,而不是您认为的自下而上。区别的原因是因为应用于画布的转换影响坐标系,而不是您的逻辑坐标。

平移translate(100, 100)将向右和向下移动您的坐标系,这看起来与向上和向左移动您的逻辑坐标非常相似。

让我们看第一个序列(我已经改变了你对transformto的使用translate):

translate(100, 100);
scale(2, 2);
translate(-100, -100);

自然地,当我们想从它的中心缩放一个对象时,我们将对象平移到 (0,0),缩放对象,然后将对象移回。上面的代码,当反向阅读时,似乎可以做到这一点。然而,事实并非如此。

当我们从上到下阅读上面的代码时,它说(假设我们从一个身份转换开始):

  1. 将上下文的 (0,0) 向右移动 100 个单位并向下移动 100 个单位。这会将其带到画布的 (100,100) 位置。
  2. 使坐标系大 2 倍。
  3. 将上下文的 (0,0) 向左移动 100 个单位并向上移动 100 个单位,基本上将其返回到其原始位置(在上下文坐标空间中,而不是画布空间中)。

缩放相对于上下文的 (0,0) 点发生,该点位于画布上的 (100,100) 处。

如果我们现在要添加您的第二个序列:

translate(200, 200);
scale(2, 2);
translate(-200, -200);

这将:

  1. 将上下文的 (0,0) 移动到坐标系的 (200,200) 位置。
  2. 使坐标系比原来大 2 倍。
  3. 将上下文的 (0,0) 返回到之前的位置(在上下文坐标空间中,而不是画布空间中)。

正如您所发现的那样,这并不能满足您的期望,因为 (200,200) 不是您想要扩展的点。请记住,所有单位都相对于上下文坐标系。所以我们需要将画布位置 (200,200) 转换为 (150,150) 的上下文坐标位置,即我们矩形的原始右下角。

所以我们将序列#2更改为:

translate(150, 150);
scale(2, 2);
translate(-150, -150);

这给了我们我们所期望的(放大矩形的右下角)。这就是我们进行逆变换的原因。

演示应用程序中,当应用程序放大时,它会获取用户鼠标所在的画布单位中的坐标,使用迄今为止的上下文变换对其进行逆变换,以获取被点击的上下文坐标空间中的位置。上下文原点移动到该位置,缩放,然后返回到它的先前位置。

参考:

于 2013-05-27T05:25:48.283 回答
1

你似乎是在想太多变换了!

这是简单的规则:

如果您应用任何一组转换,那么您必须撤消所有这些转换才能恢复到未转换的状态。

时期 !!!!

所以假设你做了这 4 个转换:

  • 做#1。context.translate(100,100);
  • 做#2。context.scale(2,2);
  • 做#3。context.translate(-20,50);
  • 做#4。context.scale(10,10);

要恢复到原始未转换状态,您必须以完全相反的顺序撤消:

  • 撤消 #4: context.scale(0.10, 0.10); // 因为我们缩放了 10 倍,我们必须取消缩放 0.10
  • 撤消 #3: context.translate(20,-50);
  • 撤消 #2: context.scale(0.50, 0.50); // 因为我们缩放了 2 倍,所以我们必须取消缩放 0.50
  • 撤消 #1: context.translate(-100,-100);

把它想象成步行到你朋友家。

你右转+左+右。

然后要回家,您必须反转:左+右+左

您必须按照与原始步行完全相反的方式撤消步行路径。

变换也是如此。

这就是为什么 !!

于 2013-05-26T09:13:29.223 回答