2015-01-02 12 views
5

getBoundingClientRect() restituisce le coordinate di un elemento sullo schermo dopo la trasformazione del. Come faccio a calcolare quelle coordinate prima che venga trasformato lo? cioè senza trasformazioni.Come calcolare getBoundingClientRect() senza considerare le trasformazioni?

Il modo più semplice che ho trovato era:

element.style.transform = 'none'; //temporarily reset the transform 
var untransformedOffset = element.getBoundingClientRect().top; //get the value 
element.style.transform = ''; //set it back 

ma che provoca lento layout di santa ragione, particolarmente evidente se fatto su molti elementi. Demo dal vivo: http://jsbin.com/nibiqogosa/1/edit?js,console,output

C'è un modo migliore?


Questo codice javascript può essere applicato a:

<div id="element"></div> 
<style> #element { transform: translateY(20px); }</style> 

E il risultato sarà 0 (escluso il margine della pagina)

Il risultato di element.getBoundingClientRect().top sarà 20 (escludendo il margine della pagina)

Edit: Risposte roundup

http://jsbin.com/kimaxojufe/1/edit?css,js,console,output

+0

Leggere la matrice di trasformazione e eseguire alcuni calcoli con i valori della matrice e il limite di delimitazione corrente. – Teemu

+2

Super hacky http://jsbin.com/sivojuluvo/1/edit?js,console,output –

+0

Interessante, se lo metti come risposta lo inviterò. È _hacky_ ma non troppo se il risultato è sempre un 'matrix' o' matrix3d' in ogni browser. –

risposta

7

posizione dell'elemento ottenere senza considerare qualsiasi trasformazione sull'elemento e sull'albero DOM:

var el = element, 
offsetLeft = 0, 
offsetTop = 0; 

do{ 
    offsetLeft += el.offsetLeft; 
    offsetTop += el.offsetTop; 

    el = el.offsetParent; 
} while(el); 

ottenere la posizione dell'elemento senza considerare trasformazione applicata ad esso ma mantenendo ogni trasformazione il DOM albero.

Per fare ciò, è possibile provare a ripristinare la trasformazione.
È necessario impostare prima transform-origin su 0,0,0 e circondare la larghezza di trasformazione (scala, rotazione) translate(50%,50%) ... translate(-50%, -50%). Ecco un esempio,
cambiamento che:

transform: scale(2) rotate(45deg) translate(20px); 
transform-origin: 50% 50%; //default value 

in

transform: translate(50%, 50%) scale(2) rotate(45deg) translate(-50%,-50%) translate(20px); 
transform-origin: 0 0 0; 

Dobbiamo farlo perché la matrice restituita da getComputedStyle() non include roba fatta con origine trasformare-. Non so davvero perché.

quindi è possibile utilizzare questo codice:

function parseTransform(transform){ 
    //add sanity check 
    return transform.split(/\(|,|\)/).slice(1,-1).map(function(v){ 
     return parseFloat(v); 
    }); 
} 

function convertCoord(transformArr, x, y, z){ 
    //add sanity checks and default values  

    if(transformArr.length == 6){ 
     //2D matrix 
     //need some math to apply inverse of matrix 
     var t = transformArr, 
      det = t[0]*t[3] - t[1]*t[2]; 
     return { 
      x: ( x*t[3] - y*t[2] + t[2]*t[5] - t[4]*t[3])/det, 
      y: (-x*t[1] + y*t[0] + t[4]*t[1] - t[0]*t[5])/det 
     } 
    } 
    else /*if (transformArr.length > 6)*/{ 
     //3D matrix 
     //haven't done the calculation to apply inverse of 4x4 matrix 
    } 
} 

var elRect = element.getBoundingClientRect(), 
    st = window.getComputedStyle(element), 

    topLeft_pos = convertCoord(
       parseTransform(st.transform), 
       elRect.left, 
       elRect.top, 
       st.perspective 
    );  

Non sto a spiegare la parte di matematica, perché penso che sia oltre la portata di questo post. Potrebbe ancora spiegarlo da un'altra parte (un'altra domanda forse?).

+0

Ripristinare la trasformazione è un po 'troppo, ma la tua prima soluzione funziona perfettamente ed efficientemente! –

+0

Non ho capito correttamente la tua domanda, pensavo volessi ripristinare la trasformazione dell'elemento ma non la trasformazione del genitore, se presente. Ho aggiornato la mia risposta per spiegare un po 'di più la differenza tra le 2 soluzioni. – Ghetolay

+0

Sì, forse era un grande vago. Il titolo menzionava "gBCR senza (nessuna) trasformazione" mentre il corpo mostrava un esempio con il solo ripristino di una trasformazione. • Per questo motivo creo il ["roundup"] (http://jsbin.com/kimaxojufe/1/edit?css,js,console,output) per includere tutte le possibili situazioni. • La prima soluzione risolve la mia _title question_ (in tutti i casi); Benjamin risolve la trasformazione di un solo livello (in tutti i casi) –

1

Mi è piaciuta la risposta di Ghetolay. L'ho usato ma l'ho reso un po 'più performante evitando il loop.

Ho una tag cloud trascinabile e devo aggiornare la posizione di trascinamento usando le trasformazioni, ma tengo traccia della posizione originale (senza trasformazione).

La risposta precedente suggeriva di eseguire il ciclo attraverso i parametri offset. Nel mio caso, e penso in molti casi, i tag vengono trasformati ma il contenitore no. Quindi devo solo ottenere il primo offsetParent e utilizzare getBoundingClientRect() lì. Non c'è bisogno di continuare ad andare in loop. Ho risolto questo problema:

var el = element; 
var parentRect = element.offsetParent.getBoundingClientRect(); 
var offsetLeft = parentRect.left + element.offsetLeft; 
var offsetTop = parentRect.top + element.offsetTop; 
+0

Non sembra corretto. L'output dovrebbe essere 8 ma è 36 http://jsbin.com/nufusupeji/edit?js,console,output –

+0

Stai utilizzando 'transform.getBounding ...', Devi usare 'transform.offsetParent.getBounfing. ..'. E, in questo caso, l'offset genitore è il corpo in quanto non vi è alcun wrapper relativamente posizionato, quindi questo metodo conterà anche il margine del corpo. Quindi, offsetParent.top è 8 e anche l'element.offsetTop è 8. Se non ci sono wrapper in mezzo, puoi usare element.offsetTop. – ezakto

+0

Ho creato una vera e propria suite di test per testare questo perché ci sono alcune variabili e possibili comportamenti desiderati (cioè genitori posizionati, trasformazioni nidificate) e il vostro sembra ancora essere spento in molti casi. http://jsbin.com/kimaxojufe/1/edit?css,js,console,output Puoi dare un'occhiata e apportare le modifiche necessarie? –

Problemi correlati