我有一个用matrix3d
转换过来的元素给它透视。它代表手持设备的屏幕。
有一个背景图像显示了手持设备本身,而且没有转换。定位保持它的元素,屏幕元素绝对定位在left: 0; top: 0;
内,然后变换,原点位于容器的左上角。这是我与背景图像完美排列的最简单方法(我使用this very handy tool来提出矩阵),并将屏幕元素从角落移开。
我希望能够通过鼠标和触摸事件与屏幕进行交互。为此,在点击或触摸事件中,我需要在屏幕元素的局部坐标系中找到坐标 - 即变换发生前的坐标。换句话说,当点击手持设备屏幕的左上角(不页面上其边界框的左上角!)时,我想要[0, 0]
,当点击屏幕的右上角时,在此转换的情况下实际上在页面上和右侧,我想要[untransformedWidth, 0]
。
鼠标事件提供offsetX
和offsetY
据称正是这样做的(下面有更多内容),但触摸事件没有这些属性,所以我需要一种方法来计算它们自己。
使用math.js我可以输入转换矩阵并将其反转。我有some code to loop over the CSS rules来获取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
和offsetY
怎么办?好吧,事实证明答案取决于浏览器。
在Firefox中,offset*
找到的坐标也与点击的位置不匹配。它们与我计算的不完全相同,但仅相差几个像素。它们与我的计算值一样远离真正的点击点。
但在Chrome中,offset*
找到的坐标非常完美。
这里是jsfiddle。
我的计算有什么问题吗?我有没有办法模仿Chrome的结果,但没有offset*
属性?
答案 0 :(得分:2)
我对这个理论并不完全确定,我在研究类似的问题时读过这个,但这就是我发现的。矩阵的乘法(三维变换的矩阵 - 齐次坐标和光标的位置)产生两个除x和y之外的值。第一个是z,另一个是w,用于在2d平面上投影3d对象。
如果将矢量的x和y值除以w坐标,则可以获得光标在笛卡尔平面上的正确位置/映射。
所以,我会用以下代码替换你的小提琴中的代码,第69-70行:
var x = result.get([0]);
var y = result.get([1]);
var w = result.get([3]);
screenCrosshair.style.left = x / w + 'px';
screenCrosshair.style.top = y / w + 'px';
这是更新小提琴: https://jsfiddle.net/x3ruc3tL/1/
然后您不需要浏览器的offsetX
和offsetY
值来查找正确的位置。
请阅读以下文章以获取更多信息:
https://en.wikipedia.org/wiki/3D_projection#Perspective_projection http://deltaorange.com/2012/03/08/the-truth-behind-homogenous-coordinates/