2016-04-02 22 views
5

Ho un elemento che è stato trasformato con un matrix3d per fornire una prospettiva. Rappresenta lo schermo di un dispositivo portatile.Ottenere la posizione toccata nelle coordinate locali di un elemento trasformato

C'è un'immagine di sfondo che mostra il dispositivo portatile stesso e questo non viene trasformato. L'elemento che contiene ciò è posizionato e l'elemento dello schermo è posizionato assolutamente al suo interno, a left: 0; top: 0; e quindi trasformato, con un'origine nella parte in alto a sinistra del contenitore. Questo è stato il modo più semplice per me di allinearlo perfettamente con l'immagine di sfondo (ho usato this very handy tool per inventare la matrice) e sposta l'elemento dello schermo lontano dall'angolo.

Voglio essere in grado di interagire con lo schermo con il mouse e toccare gli eventi. Per fare ciò, in un evento click o touch ho bisogno di trovare le coordinate nel sistema di coordinate locale dell'elemento schermo - cioè, le coordinate prima che la trasformazione abbia avuto luogo. In altre parole, quando si fa clic in alto a sinistra sullo schermo del dispositivo portatile (che è non nella parte in alto a sinistra del riquadro di delimitazione sulla pagina!) Voglio [0, 0] e quando si fa clic in alto a destra dello schermo, che in il caso di questa trasformazione è in realtà più in alto sulla pagina e sulla destra, voglio [untransformedWidth, 0].

Gli eventi del mouse forniscono offsetX eche presumibilmente fanno esattamente questo (più su quello sotto), ma gli eventi di tocco non hanno queste proprietà, quindi ho bisogno di un modo per calcolarli da solo.

Utilizzo di math.js Posso alimentare la matrice di trasformazione e invertirla. Ho some code to loop over the CSS rules per ottenere la regola transform: matrix3d(...) (non voglio ripeterlo nel mio codice se non devo), che salterò - So che funziona perché i numeri corrispondono al CSS.

Nota che i CSS ha i suoi elementi di matrice in ordine delle colonne, in modo da matrix3d(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) guarda in regolare forma matriciale come questo:

┌   ┐ 
│ a e i m │ 
│ b f j n │ 
│ c g k o │ 
│ d h l p │ 
└   ┘ 

Nel frattempo, math.js vuole le sue matrici fila dichiarati per riga, come [[a, e, i, m], [b, f, j, n]....

Quindi partenza dove ho una lista degli elementi numerici dall'interno del matrix3d(...) espressione, al fine CSS, sto costruendo e invertendo la matrice in questo modo:

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

Ho quindi creato un topo gestore di eventi sull'elemento schermo:

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

Se mi muovo un marcatore (rispetto al contenitore) a questo punto [x, y], sembra esattamente dove ho cliccato. Quindi so che questo sta funzionando. Sono quindi moltiplicare un vettore di queste coordinate per l'inverso matrice di trasformazione:

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

Se sposto un altro marcatore (questo rispetto all'elemento schermo) per i valori del vettore risultante [result.get([0]), result.get([1])] si muove a all'incirca stessa posizione come l'indicatore precedente, ma non è giusto. Sembra che più lontano dall'origine vado, maggiore è l'errore, fino a quando non è davvero molto male verso i bordi destro e inferiore.

Ma allora cosa succede se controllo contro offsetX e offsetY? Bene, si scopre che la risposta dipende dal browser.

In Firefox, le coordinate trovate con offset* non corrispondono nemmeno alla posizione selezionata. Non sono esattamente uguali a quelli calcolati, ma solo diversi di un paio di pixel. Sono tanto lontani dal vero punto cliccato quanto i miei valori calcolati.

Sample output in Firefox 45

Ma in Chrome le coordinate trovati con offset* sono perfetti.

Sample output in Chrome

Ecco un jsfiddle.

C'è qualcosa che sto facendo male con il mio calcolo? C'è un modo per me di imitare il risultato di Chrome, ma senza le proprietà offset*?

+0

ho il sospetto che la mancata corrispondenza tra '' event.offset * in Firefox e Chrome potrebbe essere un bug. Ho [segnalato sul tracker Firefox] (https://bugzilla.mozilla.org/show_bug.cgi?id=1261645). Ma se * è * un bug di Firefox, lo stesso bug esiste nel mio codice, e voglio trovarlo. – tremby

+0

Questo errore sembra essere risolto in Firefox versione 50.0.2, anche se sono ancora interessato a ciò che la soluzione reale era qui. Correzione – Thomas

+0

: posso confermare che il bug è ancora lì, i miei test erano sbagliati. Sarebbe bello se questo fosse risolto. – Thomas

risposta

2

Non sono del tutto sicuro della teoria, ho letto su questo mentre stavo lavorando su un problema simile, ma ecco quello che ho trovato. La moltiplicazione delle matrici (la matrice della trasformazione 3d - le coordinate omogenee e la posizione del cursore) produce altri due valori oltre a xey. Il primo è z, e l'altro è w che viene utilizzato per proiettare l'oggetto 3d su un piano 2D.

Se si dividono i valori xey del vettore per la coordinata w, si ottiene la posizione/mappatura corretta del cursore sul piano cartesiano.

Quindi, vorrei sostituire il codice nel vostro violino, le linee 69-70, con questi:

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

Ecco il violino aggiornamento: https://jsfiddle.net/x3ruc3tL/1/

E poi non è necessario il offsetX e offsetY valori del browser per trovare la posizione corretta.

Si prega di leggere i seguenti articoli per ulteriori informazioni:

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

+0

Ecco fatto! Grazie mille! Sembra che Mozilla abbia lo stesso identico bug nel loro codice, quindi li indirizzerò alla tua soluzione. – tremby