简体   繁体   English

变换矩阵旋转不保留局部轴缩放?

[英]Transformation matrix rotation not preserving local axis scaling?

I have a simple transform class to apply translations, scales and rotations on a div in any arbitrary order:我有一个简单的转换类,可以按任意顺序在 div 上应用平移、缩放和旋转:

class TransformDiv{

  constructor(div)
  {
    this.div = div;
    this.translateX = 0;
    this.translateY = 0;
    this.scaleX = 1;
    this.scaleY = 1;
    this.shearX = 0;
    this.shearY = 0;
  }
  
  translate(x, y)
  {
    this.translateX += x;
    this.translateY += y;
    this.setTransform();
  }
  
  scale(x, y, anchorX = 0, anchorY = 0)
  {
    this.scaleX *= x;
    this.shearX *= x;
    this.scaleY *= y;
    this.shearY *= y;
    this.translateX -= (this.translateX - anchorX) * (1 - x);
    this.translateY -= (this.translateY - anchorY) * (1 - y);
    this.setTransform();
  }
  
  rotate(rad, anchorX = 0, anchorY = 0)
  {
    let cos = Math.cos(rad);
    let sin = Math.sin(rad);
    
    // the composition of two successive rotations are additive
    let newScaleX = this.scaleX * cos + this.shearX * sin;
    let newShearX = this.scaleX * (-sin) + this.shearX * cos;
    let newShearY = this.shearY * cos + this.scaleY * sin;
    let newScaleY = this.shearY * (-sin) + this.scaleY * cos;
    this.scaleX = newScaleX;
    this.shearX = newShearX;
    this.shearY = newShearY;
    this.scaleY = newScaleY;
    //rotation about an arbitrary point
    let originX = (this.translateX - anchorX);
    let originY = (this.translateY - anchorY);
    this.translateX -= (originY * sin - originX * (cos - 1));
    this.translateY -= (-originY * (cos - 1) - originX * sin);
    
    this.setTransform();
  }
  
  setTransform()
  {
    this.div.style.transform = `matrix(${this.scaleX}, ${this.shearY}, ${this.shearX}, ${this.scaleY}, ${this.translateX}, ${this.translateY})`;
  }
}

A problem arises when I wish to rotate after a non-uniform scale has been made.当我希望在制作非均匀比例后旋转时会出现问题。

Edit - Newer interactive example: https://codepen.io/manstie/pen/RwGGOmB编辑 - 较新的交互式示例: https : //codepen.io/manstie/pen/RwGGOmB

Here is the example I made: https://jsfiddle.net/ft61q230/1/这是我制作的示例: https : //jsfiddle.net/ft61q230/1/

In the example here:在这里的例子中:

div2.translate(100, 100);
div2.scale(2, 1, 100, 100);
div2.rotate(Math.PI / 2, 100, 100);

The expected result is for Test 1 Text and Test 2 Text to be the same length, as if you were rotating from the top left of the div clockwise 90 degrees;预期的结果是Test 1 TextTest 2 Text的长度相同,就像您从 div 的左上角顺时针旋转 90 度一样; but as you can see the result is such that the rotation logic I am performing retains the scale on the world-space axis, so now Test 2 Text is twice as tall rather than twice as long.但是正如您所看到的,结果是我正在执行的旋转逻辑保留了世界空间轴上的比例,所以现在Test 2 Text高度是原来的两倍,而不是长度的两倍。

Current outcome:当前结果:

当前结果

Desired outcome:期望的结果:

期望的结果

The current rotation logic is based on multiplying the existing transformation matrix that makes up rotation by another transformation matrix containing an angle to rotate by, but I realize it is not as simple as that and I am missing something to retain local-axial scale.当前的旋转逻辑基于将构成旋转的现有变换矩阵乘以另一个包含旋转角度的变换矩阵,但我意识到它并不那么简单,而且我缺少一些东西来保留局部轴尺度。

Thank you for your assistance.谢谢您的帮助。

Edit:编辑:

Was recommended DOMMatrix which does all this math for me, but it has the same problem, although there is some skew which I don't think is accurate:推荐使用DOMMatrix它为我完成所有这些数学运算,但它有同样的问题,尽管有一些我认为不准确的偏差:

https://jsfiddle.net/heqo7vrt/1/ https://jsfiddle.net/heqo7vrt/1/

The skew is caused by the scale function scaling it's local X axis while it is rotated , and then rotating after not keeping that local X axis scaling.倾斜是由缩放函数在旋转时缩放其局部 X 轴,然后在不保持该局部 X 轴缩放后旋转引起的。 Also, DOMMatrix translate function has the translations apply on its local axis which is not desired in my situation but if its rotate function worked as expected I would be able to use it.此外, DOMMatrix translate 函数将平移应用于其局部轴,这在我的情况下是不需要的,但如果它的旋转函数按预期工作,我将能够使用它。

I managed to fix it here:我设法在这里修复它:

Regular: https://jsfiddle.net/sbca61k5/常规: https : //jsfiddle.net/sbca61k5/

let newScaleX = cos * this.scaleX + sin * this.shearY;
let newShearX = cos * this.shearX + sin * this.scaleY;
let newShearY = -sin * this.scaleX + cos * this.shearY;
let newScaleY = -sin * this.shearX + cos * this.scaleY;

DOMMatrix version: https://jsfiddle.net/b36kqrsg/ DOMMatrix 版本: https ://jsfiddle.net/b36kqrsg/

this.matrix = new DOMMatrix([cos, sin, -sin, cos, 0, 0]).multiply(this.matrix);
// or
this.matrix = new DOMMatrix().rotate(deg).multiply(this.matrix);

The difference is to have the rotation matrix multiplied by the rest of the matrix to "add" it on, not the other way round:不同之处在于将旋转矩阵乘以矩阵的其余部分以“添加”它,而不是相反:

[a c e]   [cos -sin 0]   [scx shy tx]
[b d f] = [sin  cos 0] . [shx scy ty]
[0 0 1]   [0    0   1]   [0   0   1 ]

I'm unsure about the details of the anchor mathematics but the DOMMatrix version's anchor is relative to its own top left whereas the other is relative to the top left of the document.我不确定锚点数学的细节,但 DOMMatrix 版本的锚点是相对于它自己的左上角,而另一个是相对于文档左上角的。

From my interactive example the anchor maths does not work as after a multitude of rotations the objects get further away from the anchor origin.从我的交互式示例中,锚数学不起作用,因为在多次旋转后,对象离锚原点更远。 https://codepen.io/manstie/pen/PoGXMed https://codepen.io/manstie/pen/PoGXMed

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM