getBoundingClientRect()返回屏幕上的元素的坐标之后被转化.如何在变换之前计算这些坐标?即没有变形.
我找到的最简单的方法是:
element.style.transform = 'none'; //temporarily reset the transform
var untransformedOffset = element.getBoundingClientRect().top; //get the value
element.style.transform = ''; //set it back
Run Code Online (Sandbox Code Playgroud)
但这会导致布局抖动缓慢,特别是如果在许多元素上完成则会引起注意.现场演示:http://jsbin.com/nibiqogosa/1/edit?js,console,output
有没有更好的办法?
该javascript代码可以应用于:
<div id="element"></div>
<style> #element { transform: translateY(20px); }</style>
Run Code Online (Sandbox Code Playgroud)
结果将为0(不包括页面的边距)
结果element.getBoundingClientRect().top将是20(不包括页面的边距)
Ghe*_*lay 12
获取元素位置而不考虑对元素和DOM树的任何转换:
var el = element,
offsetLeft = 0,
offsetTop = 0;
do{
offsetLeft += el.offsetLeft;
offsetTop += el.offsetTop;
el = el.offsetParent;
} while( el );
Run Code Online (Sandbox Code Playgroud)
获取元素位置而不考虑应用于它的转换,但保持DOM树的任何转换.
为此,您可以尝试还原转换.
你必须首先设置transform-origin到0,0,0和自己周围的变换(缩放,旋转)宽translate(50%,50%) ... translate(-50%, -50%).这是一个例子,
改变:
transform: scale(2) rotate(45deg) translate(20px);
transform-origin: 50% 50%; //default value
Run Code Online (Sandbox Code Playgroud)
成
transform: translate(50%, 50%) scale(2) rotate(45deg) translate(-50%,-50%) translate(20px);
transform-origin: 0 0 0;
Run Code Online (Sandbox Code Playgroud)
我们需要这样做,因为getComputedStyle()返回的矩阵不包含使用transform-origin完成的内容.不知道为什么.
然后你可以使用这段代码:
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
);
Run Code Online (Sandbox Code Playgroud)
我不会解释数学部分,因为我认为这超出了本文的范围.仍然可以在其他地方解释它(可能是另一个问题?).
上面在数学上反转变换的答案是一个很好的尝试,但并不完全正确(并且比它需要的更复杂)。更正确的反转如下。
这并没有考虑倾斜或旋转平移,但至少在使用缩放时它会产生正确的边缘位置,并且在没有变换时不会造成太大的性能损失。
即使使用 scale(0) 也能产生准确的结果(尽管在宽度/高度上丢失了子像素精度)。
请注意,打开软件键盘的 iOS 在getBoundingClientRect()和offsetTop/ offsetLeft-之间产生不同的结果,后者在任何浏览器上都不支持亚像素精度。这产生与 一致的结果getBoundingClientRect()。
function adjustedBoundingRect(el) {
var rect = el.getBoundingClientRect();
var style = getComputedStyle(el);
var tx = style.transform;
if (tx) {
var sx, sy, dx, dy;
if (tx.startsWith('matrix3d(')) {
var ta = tx.slice(9,-1).split(/, /);
sx = +ta[0];
sy = +ta[5];
dx = +ta[12];
dy = +ta[13];
} else if (tx.startsWith('matrix(')) {
var ta = tx.slice(7,-1).split(/, /);
sx = +ta[0];
sy = +ta[3];
dx = +ta[4];
dy = +ta[5];
} else {
return rect;
}
var to = style.transformOrigin;
var x = rect.x - dx - (1 - sx) * parseFloat(to);
var y = rect.y - dy - (1 - sy) * parseFloat(to.slice(to.indexOf(' ') + 1));
var w = sx ? rect.width / sx : el.offsetWidth;
var h = sy ? rect.height / sy : el.offsetHeight;
return {
x: x, y: y, width: w, height: h, top: y, right: x + w, bottom: y + h, left: x
};
} else {
return rect;
}
}
var div = document.querySelector('div');
console.log(div.getBoundingClientRect(), adjustedBoundingRect(div));
div.classList.add('transformed');
console.log(div.getBoundingClientRect(), adjustedBoundingRect(div));Run Code Online (Sandbox Code Playgroud)
.transformed {
transform: translate(8px,8px) scale(0.5);
transform-origin: 16px 16px;
}Run Code Online (Sandbox Code Playgroud)
<div>Hello</div>Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8120 次 |
| 最近记录: |