获取已转换元素的本地坐标中的触摸位置

时间:2016-04-02 12:15:33

标签: javascript cross-browser css-transforms mathjs

我有一个用matrix3d转换过来的元素给它透视。它代表手持设备的屏幕。

有一个背景图像显示了手持设备本身,而且没有转换。定位保持它的元素,屏幕元素绝对定位在left: 0; top: 0;内,然后变换,原点位于容器的左上角。这是我与背景图像完美排列的最简单方法(我使用this very handy tool来提出矩阵),并将屏幕元素从角落移开。

我希望能够通过鼠标和触摸事件与屏幕进行交互。为此,在点击或触摸事件中,我需要在屏幕元素的局部坐标系中找到坐标 - 即变换发生前的坐标。换句话说,当点击手持设备屏幕的左上角(页面上其边界框的左上角!)时,我想要[0, 0] ,当点击屏幕的右上角时,在此转换的情况下实际上在页面上和右侧,我想要[untransformedWidth, 0]

鼠标事件提供offsetXoffsetY据称正是这样做的(下面有更多内容),但触摸事件没有这些属性,所以我需要一种方法来计算它们自己。

使用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])],它会移动到大致与前一个标记相同的位置,但它并不完全正确。似乎距离原点越远,误差就越大,直到它对右边和底边都非常糟糕。

但是如果我检查offsetXoffsetY怎么办?好吧,事实证明答案取决于浏览器。

在Firefox中,offset*找到的坐标也与点击的位置不匹配。它们与我计算的不完全相同,但仅相差几个像素。它们与我的计算值一样远离真正的点击点。

Sample output in Firefox 45

但在Chrome中,offset*找到的坐标非常完美。

Sample output in Chrome

这里是jsfiddle

我的计算有什么问题吗?我有没有办法模仿Chrome的结果,但没有offset*属性?

1 个答案:

答案 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/

然后您不需要浏览器的offsetXoffsetY值来查找正确的位置。

请阅读以下文章以获取更多信息:

https://en.wikipedia.org/wiki/3D_projection#Perspective_projection http://deltaorange.com/2012/03/08/the-truth-behind-homogenous-coordinates/