Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
388 views
in Technique[技术] by (71.8m points)

javascript - How to compute getBoundingClientRect() without considering transforms?

getBoundingClientRect() returns the coordinates of an element on the screen after being transformed. How do I calculate those coordinates before being transformed? i.e. without transforms.

The simplest way I found was:

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

but that causes slow layout thrashing, especially noticeable if done on many elements. Live demo: http://jsbin.com/nibiqogosa/1/edit?js,console,output

Is there a better way?


That javascript code can be applied to:

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

And the result will be 0 (excluding the page's margin)

The result of element.getBoundingClientRect().top will be 20 (excluding the page's margin)

Edit: Answers roundup

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Get element position without considering any transformation on the element and up the DOM tree :

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

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

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

Get element position without considering transformation applied to it but keeping any transformation up the DOM tree.

To do so you could try to revert the transform.
You must first set transform-origin to 0,0,0 and surround yourself your transformation (scale, rotate) width translate(50%,50%) ... translate(-50%, -50%). Here is an example,
change that :

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

into

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

We need to do that because the matrix returned by getComputedStyle() does not include stuff done with transform-origin. Don't know really why.

Then you can use this code :

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

I won't explain the math part because I think it's beyond the scope of this post. Could still explain it somewhere else (another question maybe ? ).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...