问题描述
我有一个简单的转换类,可以以任意顺序在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,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})`;
}
}
当我希望在制作不均匀的刻度后旋转时会出现问题。
编辑-较新的交互式示例:https://codepen.io/manstie/pen/RwGGOmB
这是我做的例子: https://jsfiddle.net/ft61q230/1/
在此处的示例中:
div2.translate(100,100);
div2.scale(2,1,100,100);
div2.rotate(Math.PI / 2,100);
预期的结果是Test 1 Text
和Test 2 Text
的长度相同,就像您从div的左上角顺时针旋转90度一样;但是正如您所看到的那样,结果是我执行的旋转逻辑在世界空间轴上保留了比例,所以现在Test 2 Text
的高度是其两倍,而不是长度的两倍。
当前结果:
所需结果:
当前的旋转逻辑是基于将构成旋转的现有变换矩阵与另一个包含旋转角度的变换矩阵相乘的,但是我意识到这并不那么简单,并且我丢失了一些保留局部轴向的东西规模。
谢谢您的帮助。
编辑:
建议使用DOMMatrix
来为我完成所有这些数学运算,但是它存在相同的问题,尽管存在一些我认为不正确的偏斜:
https://jsfiddle.net/1tk9fcau/2/
偏斜是由缩放功能在旋转时沿其局部X轴缩放引起的,然后在不保持该局部X轴缩放后旋转。此外,DOMMatrix
翻译功能可将平移应用到不需要的本地轴上。
解决方法
我在这里修复了它:
常规: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 版本:https://jsfiddle.net/b36kqrsg/
this.matrix = new DOMMatrix([cos,sin,-sin,cos,0]).multiply(this.matrix);
// or
this.matrix = new DOMMatrix().rotate(deg).multiply(this.matrix);
区别在于将旋转矩阵乘以矩阵的其余部分以“添加”它,而不是相反:
[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 ]
我不确定锚点数学的细节,但 DOMMatrix 版本的锚点是相对于它自己的左上角,而另一个是相对于文档左上角的。
从我的交互式示例中,锚数学不起作用,因为在多次旋转后,对象离锚原点更远。 https://codepen.io/manstie/pen/PoGXMed