【问题标题】:How to get transform matrix of a DOM element?如何获取 DOM 元素的变换矩阵?
【发布时间】:2022-01-13 12:10:14
【问题描述】:

如何获取 DOM 元素的变换矩阵?就像画布上下文一样,我们在 DOM 上有一个 getTransform() 方法吗?

【问题讨论】:

标签: javascript html web canvas


【解决方案1】:

getComputedStyle($el).transform 应该返回一个 2 x 3 变换矩阵,如果有任何 2d 变换,如果在 z 轴上也有变换,则返回 4 x 4 matrix3d。请参阅my answer here 了解如何操作它以防万一。

在嵌套元素的情况下,生成的矩阵是从父级到子级的矩阵相乘:

<parent>
  <firstChild>
    <grandChild>
    </grandChild>
  </firstChild>
</parent>

生成的 DOMMatrix 上方是:

Mparent • MfirstChild • MgrandChild

transform 属性中的上述 svg 等价物是:

transform="transformparent transformfirstChildtransform grandChild

所以变换是逻辑上RIGHT应用到LEFT(grandChild的变换首先应用于自身),其数学投影是将矩阵从 LEFT 乘到 RIGHT,如上面的 所示。

但是有一个问题,在 SVG 中,所有的转换都是相对于 viewBox 的绝对 0,0 原点(左上角),无论元素在哪里/是什么。为此,我们需要一些特殊条件:

1- 使用transform-origin: 0px 0px;:

在 CSS 中,一些转换,比如旋转是相对于元素的中心,以使人们更容易做事。然而,这会产生“旋转(Xdeg)”相对于元素的宽度/高度的问题(默认 css 旋转大约为 50% 50%)。边长为 100 像素的正方形上的默认 css“旋转(40 度)”不会产生与 300 像素的正方形相同的矩阵。为了不包含元素的可变宽度/高度,我们可以做的一件事是确保元素使用transform-origin: 0px 0px;

2- 仅使用 transform 并且没有 css 定位:

确保相对/绝对定位元素不包含修改元素位置的值,例如left, top, bot, right ...,因为这些不会被捕获到元素的转换矩阵中,除非您手动调用el.getBoundingClientRect() 并导出矩阵自己输入

如果满足这些条件,下面的示例 (also fiddle) 应该可以正常工作。目标是计算红色(父)+蓝色(子)正方形的组合矩阵,并将洋红色/黄色与蓝色正方形叠加。洋红色的矩阵是通过将红色和蓝色的矩阵从左到右、黄色的从右到左相乘来计算的:

/*THIS EXAMPLE WORKS IF THE MECHANISM OF TRANSFORMATION MATRICES 
IN CSS DOM IS CLOSE ENOUGH TO SVG, 
WHICH MEANS LIKE IN SVG, ROTATE SHOULD NOT 
BE AROUND AN ELEMENTS CENTER (default) BUT TO 
THE TOP LEFT CORNER OF THE ELEMENT. 
IN SVG, ALL TRANFORMATIONS ARE RELATIVE TO THE TOP LEFT 
CORNER OF THE VIEWBOX WHICH CANNOT 
BE 100% TRANSPORTED TO CSS. 
THE CLOSEST I COULD FIND WAS TO 
SET TRANSFORM-ORIGIN: 0px 0px WHICH SEEMS TO WORK */

/*There is red parent square and blue child square 
inside which both have cascading transformations. 
The goal is to combine their transformation matrices 
and make yellow (W) and magenta (U) squares 
superimpose with the blue square*/

//get all the elements in variables
const [elX, elY, elU, elW] = [...document.getElementsByTagName("div")];
//make a regexp to extract the entries of the matrix, with match, it returns [a, b, c, d, e, f]
const rgx = /(-?[0-9]+(?:\.[0-9]+)?)/gi;
//DOMMatrices expect an array of 6 numbers for 2D transformations
//get the matrix of big red parent square
const matrix_elX = new DOMMatrix(getComputedStyle(elX).transform.match(rgx));
//get the matrix of the small blue child square
const matrix_elY = new DOMMatrix(getComputedStyle(elY).transform.match(rgx));
/*
'result' is MX * MY and 'result2' is MY * MX
magenta square U uses result,
yellow square W uses result2,
magenta square U correctly superimposes with blue square,
but yellow square W does NOT,
therefore MX * MY and MY * MX are not the same, as you can expect that matrice multiplication is NOT commutative
Below uses DOMMatrix.fromMatrix is create clones of the matrices because operations does mutate the matrix itself.
*/
let result = DOMMatrix.fromMatrix(matrix_elY).preMultiplySelf(DOMMatrix.fromMatrix(matrix_elX)),
        result2 = DOMMatrix.fromMatrix(matrix_elX).preMultiplySelf(DOMMatrix.fromMatrix(matrix_elY));
elU.style.transform = result;
elW.style.transform = result2;
* {
  margin: 0px;
  box-sizing: border-box;
  transform-origin: 0px 0px;
}

#x {
  position: relative;
  background: red;
  width: 300px;
  height: 300px;
  transform: translate(-20px, -20px) rotate(-30deg) translate(150px, 100px) rotate(20deg);
}

#y {
  position: relative;
  background: blue;
  width: 100px;
  height: 100px;
  transform: rotate(-5deg) rotate(10deg) translate(200px, 50px) rotate(75deg) translate(35px, 10px) rotate(-25deg);
}

#u {
  position: absolute;
  top: 0px;
  width: 100px;
  height: 100px;
  background: magenta;
  opacity: 0.5;
}

#w {
  position: absolute;
  top: 0px;
  width: 100px;
  height: 100px;
  background: yellow;
  opacity: 0.5;
}
<div id="x">
  <div id="y">
    
  </div>
</div>

<div id="u">U</div>
<div id="w">W</div>

【讨论】:

  • 可能有,就像在 SVG 中一样,这是一个过去对我有用的“不太干净”的解决方案。
  • 至少我发现两个 npm 包暴露了一些可靠的方法。谢谢
  • 他们可能正在解析“matrix(”或“matrix3d”,所以它要么是在解析地狱,要么是在技术债务:)
  • 相比我们编写的任何代码,都在浏览器中进行了“更多”测试。清单上少了一项。
  • 感谢您的帮助! @易卜拉欣
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多