Skip to content

Instantly share code, notes, and snippets.

@andreiglingeanu
Last active November 29, 2024 10:24
Show Gist options
  • Save andreiglingeanu/aa3aef6c8dffb2105736148b2cab3617 to your computer and use it in GitHub Desktop.
Save andreiglingeanu/aa3aef6c8dffb2105736148b2cab3617 to your computer and use it in GitHub Desktop.
A nice way to nullify CSS transforms and get original positions of the un-modified rect
// Nullify the transforms of the element
//
// This is all behaving just like getBoundingClientRect() but it nullifies all the transforms
// and kinds _reverts_ the element onto its original position.
// This will work even with complex translations, rotations.
// The beauty is in the way it applies matrix inverse onto the transformation
// matrix and mutates the getboundingclientrect along the way.
//
// Usage:
// let { top, left } = nullifyTransforms(el);
//
// TODO:
// can probably be extended to be having width/height support too but
// that is not mandatory, I only needed top/left for my use case
//
// Props to that awesome answer on StackOverflow
// https://stackoverflow.com/questions/27745438/how-to-compute-getboundingclientrect-without-considering-transforms
function nullifyTransforms(el) {
//add sanity checks and default values
let {top, left, width, height} = el.getBoundingClientRect()
let transformArr = parseTransform(el)
if (transformArr.length == 6) {
// 2D matrix
// need some math to apply inverse of matrix
// That is the matrix of the transformation of the element
// a scale x
// b shear y
// c shear x
// d scale y
// e translate x
// f translate y
var t = transformArr
let det = t[0] * t[3] - t[1] * t[2]
return {
width: width / t[0],
height: height / t[3],
left:
(left * t[3] - top * t[2] + t[2] * t[5] - t[4] * t[3]) /
det,
top:
(-left * t[1] + top * 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
return {top, left, width, height}
}
function parseTransform(el) {
let transform = window.getComputedStyle(el).transform
//add sanity check
return transform
.split(/\(|,|\)/)
.slice(1, -1)
.map(function(v) {
return parseFloat(v)
})
}
}
@hirasso
Copy link

hirasso commented May 2, 2019

Never mind, just found out about .offsetLeft and .offsetTop ;)

@Tofandel
Copy link

.split(/[(,)]/)

@andreiglingeanu
Copy link
Author

@Tofandel got any specific case that needs to be covered? Feel free to send an executable case and I'll fix the initial gist. Thanks!

@Tofandel
Copy link

It's the same regex, just one that is more readable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment