获取已转换元素的局部坐标中的触摸位置
Getting the touched position in local coordinates of a transformed element
我有一个元素已经用 matrix3d
进行了转换以赋予它视角。它代表手持设备的屏幕。
有一张显示手持设备本身的背景图片,未进行转换。放置它的元素被定位,屏幕元素绝对定位在其中,在 left: 0; top: 0;
,然后转换,原点在容器的左上角。这是我将其与背景图像完美对齐的最简单方法(我使用 this very handy tool 得出矩阵),并将屏幕元素从角落移开。
我希望能够通过鼠标和触摸事件与屏幕进行交互。为此,在单击或触摸事件时,我需要在屏幕元素的局部坐标系中找到坐标——即发生变换之前的坐标。换句话说,当单击手持设备屏幕的左上角( 而不是 页面上其边界框的左上角!)时,我想要 [0, 0]
,当单击屏幕的右上角时,在这个转换的情况下,它实际上在页面的上方和右侧,我想要 [untransformedWidth, 0]
.
鼠标事件提供 offsetX
和 offsetY
据称正是这样做的(更多内容见下文),但触摸事件没有这些属性,所以我需要一种方法来计算它们我自己。
使用 math.js I can feed in the transformation matrix and invert it. I have 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*
属性?
我不完全确定这个理论,我在处理类似问题时读到过这个,但这是我发现的。矩阵的乘法(3d 变换的矩阵 - 齐次坐标和光标的位置)除了 x 和 y 之外还产生两个值。第一个是 z,另一个是 w,用于将 3d 对象投影到 2d 平面上。
如果用向量的 x 和 y 值除以 w 坐标,就会得到笛卡尔平面上光标的正确 position/mapping。
所以,我会用这些替换您 fiddle 中第 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';
这里是更新fiddle:
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/
我有一个元素已经用 matrix3d
进行了转换以赋予它视角。它代表手持设备的屏幕。
有一张显示手持设备本身的背景图片,未进行转换。放置它的元素被定位,屏幕元素绝对定位在其中,在 left: 0; top: 0;
,然后转换,原点在容器的左上角。这是我将其与背景图像完美对齐的最简单方法(我使用 this very handy tool 得出矩阵),并将屏幕元素从角落移开。
我希望能够通过鼠标和触摸事件与屏幕进行交互。为此,在单击或触摸事件时,我需要在屏幕元素的局部坐标系中找到坐标——即发生变换之前的坐标。换句话说,当单击手持设备屏幕的左上角( 而不是 页面上其边界框的左上角!)时,我想要 [0, 0]
,当单击屏幕的右上角时,在这个转换的情况下,它实际上在页面的上方和右侧,我想要 [untransformedWidth, 0]
.
鼠标事件提供 offsetX
和 offsetY
据称正是这样做的(更多内容见下文),但触摸事件没有这些属性,所以我需要一种方法来计算它们我自己。
使用 math.js I can feed in the transformation matrix and invert it. I have 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*
属性?
我不完全确定这个理论,我在处理类似问题时读到过这个,但这是我发现的。矩阵的乘法(3d 变换的矩阵 - 齐次坐标和光标的位置)除了 x 和 y 之外还产生两个值。第一个是 z,另一个是 w,用于将 3d 对象投影到 2d 平面上。
如果用向量的 x 和 y 值除以 w 坐标,就会得到笛卡尔平面上光标的正确 position/mapping。
所以,我会用这些替换您 fiddle 中第 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';
这里是更新fiddle: 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/