我有一个元素,它已经用 a 进行了转换matrix3d
以赋予它透视。它代表手持设备的屏幕。
有一个显示手持设备本身的背景图像,这没有被转换。保存它的元素被定位,屏幕元素绝对定位在其中,在 处left: 0; top: 0;
,然后转换,原点在容器的左上角。这是我将它与背景图像完美对齐的最简单方法(我使用这个非常方便的工具来制作矩阵),并且它将屏幕元素从角落移开。
我希望能够通过鼠标和触摸事件与屏幕进行交互。为此,在单击或触摸事件中,我需要在屏幕元素的本地坐标系中找到坐标——即发生变换之前的坐标。换句话说,当单击手持设备屏幕的左上角(不是页面上边界框的左上角!)[0, 0]
时,当单击屏幕的右上角时,在这种变换的情况下实际上在页面的上方和右侧,我想要[untransformedWidth, 0]
。
鼠标事件提供offsetX
并且offsetY
据称确实做到了这一点(更多内容见下文),但触摸事件没有这些属性,所以我需要一种自己计算它们的方法。
使用math.js我可以输入转换矩阵并将其反转。我有一些代码可以遍历 CSS 规则以获取transform: matrix3d(...)
规则(如果不需要,我不想在我的代码中重复它),我会跳过它——我知道它有效,因为数字匹配 CSS。
请注意,CSS 的矩阵元素按列顺序排列,因此matrix3d(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
使用常规矩阵表示法如下所示:
┌ ┐
│ a e i m │
│ b f j n │
│ c g k o │
│ d h l p │
└ ┘
同时,math.js 希望它的矩阵逐行声明,比如[[a, e, i, m], [b, f, j, n]...
.
因此,从matrix3d(...)
表达式内部的数字元素列表开始,按照 CSS 顺序,我正在构建和反转矩阵,如下所示:
var rows = [[], [], [], []];
for (var i = 0; i < elements.length; i++) {
rows[i % 4][Math.floor(i / 4)] = elements[i];
}
var matrixTransform = math.matrix(rows);
var invertedMatrixTransform = math.inv(matrixTransform);
然后我在屏幕元素上设置了一个鼠标事件处理程序:
screen.addEventListener('mousedown', function (event) {
var rect = container.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
如果我将标记(相对于容器)移动到这一点[x, y]
,它就会出现在我单击的位置。所以我知道这很有效。然后我将这些坐标的向量乘以逆变换矩阵:
var vector = math.matrix([x, y, 0, 1]);
var result = math.multiply(inverseMatrixTransform, vector);
如果我将另一个标记(这个相对于屏幕元素的标记)移动到结果向量的值[result.get([0]), result.get([1])]
,它会移动到与前一个标记大致相同的位置,但它并不完全正确。似乎离原点越远,错误就越多,直到右侧和底部边缘确实非常糟糕。
但是,如果我检查offsetX
andoffsetY
怎么办?好吧,事实证明答案取决于浏览器。
在 Firefox 中,找到的坐标offset*
也不匹配点击的位置。它们与我计算的不完全相同,但仅相差几个像素。它们与我的计算值一样远离真正的点击点。
但在 Chrome 中找到的坐标offset*
是完美的。
这是一个jsfiddle。
我的计算有什么问题吗?有没有办法让我模仿 Chrome 的结果,但没有offset*
属性?