Getting a touch in the local coordinates of the element being transformed

I have an element that has been converted using matrix3d to give it perspective. It is a screen of a portable device.

There is a background image showing the device of the handheld device itself, and this is not converted. The element that holds this is positioned, and the screen element is located absolutely inside it, in left: 0; top: 0; left: 0; top: 0; , and then converted to the beginning in the upper left corner of the container. It was the easiest way for me to perfectly match the background image (I used this very convenient tool to come up with a matrix), and it moves the screen element away from the corner.

I want to be able to interact with the screen with the mouse and touch events. To do this, in an event with a click or touch, I need to find the coordinates in the local coordinate system of the screen element, that is, the coordinates before the conversion. In other words, when I click on the upper left screen of the portable device (which is not the upper left edge of its bounding box on the page!), I want [0, 0] , and when I click in the upper right corner of the screen, which in this case converts to In fact, both on the page and to the right, I want [untransformedWidth, 0] .

Mouse events provide offsetX and offsetY , which supposedly do just that (more on this below), but touch events do not have these properties, so I need a way to calculate them myself.

Using math.js I can feed into the transformation matrix and invert it. I have some code to transform: matrix3d(...) through the CSS rules to get the transform: matrix3d(...) rule (I don't want to repeat it in my code if I don't need it), which I will skip - I know this works, because that numbers match CSS.

Note that CSS has its matrix elements in column order, so matrix3d(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) looks like in in the form of a regular matrix as follows:

 β”Œ ┐ β”‚ aeim β”‚ β”‚ bfjn β”‚ β”‚ cgko β”‚ β”‚ dhlp β”‚ β”” β”˜ 

Meanwhile, math.js wants its matrices to be declared line by line, for example [[a, e, i, m], [b, f, j, n]...

So, starting from where I have a list of number elements from the matrix3d(...) expression, in CSS order, I build and invert the matrix as follows:

 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); 

Then I installed the mouse event handler on the screen element:

 screen.addEventListener('mousedown', function (event) { var rect = container.getBoundingClientRect(); var x = event.clientX - rect.left; var y = event.clientY - rect.top; 

If I move the marker (relative to the container) to this point [x, y] , it appears exactly where I clicked. Therefore, I know that this works. Then I multiply the vector of these coordinates by the inverse transformation matrix:

  var vector = math.matrix([x, y, 0, 1]); var result = math.multiply(inverseMatrixTransform, vector); 

If I translate another marker (this one relative to the screen element) to the resulting vector values [result.get([0]), result.get([1])] , it moves to approximately the same position as the previous marker, but this is not entirely correct. It seems that the farther from the origin I go, the more errors there are until it’s completely bad to the right and bottom edges.

But then if I check for offsetX and offsetY ? Well, it turns out that the answer is browser dependent.

In Firefox, the coordinates found using offset* also do not match the pressed position. They are not quite the same as my estimates, but only different by a couple of pixels. They are as far from the true click point as my calculated values.

Output Example in Firefox 45

But in Chrome, the coordinates found using offset* are perfect.

Chrome output example

Here's the jsfiddle .

Is there something that I am doing wrong with my calculations? Is there a way to change the image of Chrome, but without offset* properties?

+5
source share
1 answer

I'm not quite sure about the theory, I read about it while I was working on a similar problem, but here's what I found. Matrix multiplication (3d transformation matrix β€” homogeneous coordinates and cursor position) gives two more values ​​except x and y. The first is z, and the other is w, which is used to project a 3d object onto the 2d plane.

If you separate the x and y values ​​of the vector by the w coordinate, you will get the correct cursor position / display on the Cartesian plane.

So, I would replace the code in your violin, lines 69-70, with these:

 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'; 

Here is the update script: https://jsfiddle.net/x3ruc3tL/1/

And then you do not need the offsetX and offsetY values ​​of the browser to find the correct position.

Read the following articles for more information:

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

+2
source

Source: https://habr.com/ru/post/1246297/


All Articles