Get Actual Pixel Coordinates of Div After CSS3 Transform

Get actual pixel coordinates of div after CSS3 transform

After hours trying to calculate all the transformations and almost giving up desperately I came up with a simple yet genius little hack that makes it incredibly easy to get the corner points of the transformed <div />

I just added four handles inside the div that are positioned in the corners but invisible to see:

<div id="div">
<div class="handle nw"></div>
<div class="handle ne"></div>
<div class="handle se"></div>
<div class="handle sw"></div>
</div>

.handle {
background: none;
height: 0px;
position: absolute;
width: 0px;
}
.handle.nw {
left: 0;
top: 0;
}
.handle.ne {
right: 0;
top: 0;
}
.handle.se {
right: 0;
bottom: 0;
}
.handle.sw {
left: 0;
bottom: 0;
}

Now with jQuery (or pure js) it's a piece of cake to retrieve the position:

$(".handle.se").offset()

Is there a way calculate the ending position of an element before css transform matrix is applied?

As mentioned in the comments, you have to simply multiply the matrix (you can do just the 2 x 2 version, and then add the translation variables.) But as mentioned by Olivier, you need to first translate the coordinates, and then translate them back. This is easy enough by just calculating the center.

I would write this as a function from a rectangle and matrix (as a flat array of the six variables used for the CSS transforms) into another rectangle, either a simple {left, right, top, bottom} rectangle or if you want, a DomRect.

You can see it in this snippet (easier to see if you expand it with the "full page" link):

const transformRect = (rect, matrix) => {
const {left, top, right, bottom} = rect
const [a, b, c, d, tx, ty] = matrix
const dx = (left + right) / 2, dy = (top + bottom) / 2
const newCorners = [[left, top], [right, top], [right, bottom], [left, bottom]]
.map (([x, y]) => [
a * (x - dx) + c * (y - dy) + tx + dx,
b * (x - dx) + d * (y - dy) + ty + dy
])
const _left = Math .min (... newCorners .map (p => p [0]))
const _right = Math .max (... newCorners .map (p => p [0]))
const _top = Math .min (... newCorners .map (p => p [1]))
const _bottom = Math .max (... newCorners .map (p => p [1]))

return DOMRect.fromRect (
{x: _left, y: _top, width: _right - _left, height: _bottom - _top}
) // or just
// return {x: _left, y: _top, width: _right - _left, height: _bottom - _top}
}

const div = document .getElementById ('d2')
const rect = div.getBoundingClientRect()
console .log ('Before:' , rect)

const matrix = [2, 1, -1, 2, 200, 400]
div.style.transform = `matrix(${matrix .join (', ')})`

console .log ('After:', transformRect (rect, matrix))
.box {
height: 50px;
width: 50px;
background: green;
color: white;
}
#d1 {
background: #ccc;
position: absolute;
top: 8;
left: 8;
}
<div id="d1" class="box">shadow</div>
<div id="d2" class="box">content</div>

Get div size with JS after CSS3 transformation

You can use getBoundingClientRect to get the dimensions and positions after the transformation.

Simply, transform your elements, and:

$('#after')[0].getBoundingClientRect();
// note the [0], the function is DOM not jQuery's.

The best thing is that this will also return proper positions, dimensions after every transformation you apply.

You are free to rotate, skew, translate and everything else what CSS provides. gBCR will handle it.

How to get the position of element transformed with css rotate

Per your current Question and your requested confirmation of:

var x = termin.top + Math.cos(angle) * div.height;
var y = div.left + Math.sin(angle) * div.height;

The solution can be found in this other SO Answer for a different question, enhanced here:

// return an object with full width/height (including borders), top/bottom coordinates
var getPositionData = function(el) {
return $.extend({
width: el.outerWidth(false),
height: el.outerHeight(false)
}, el.offset());
};

// get rotated dimensions
var transformedDimensions = function(el, angle) {
var dimensions = getPositionData(el);
return {
width: dimensions.width + Math.ceil(dimensions.width * Math.cos(angle)),
height: dimensions.height + Math.ceil(dimensions.height * Math.cos(angle))
};
};



Here's an interactive jsFiddle that provides real-time updates for getPositionData(); function.
You'll be able to see the top and left values at the end of the CSS3 Rotation process you control.

Reference:   jsFiddle

Status Update: The above jsFiddle works great for 0-90deg and can be approved upon for all angles and different units such as rad, grad, and turn.

XY coordinates of rotated HTML elements corners

I've found a simple solution, with no extra math... I create a new element of 0px width to represent a point. I place it in the corner which I want to measure to, then ask for the viewport coordinates with getBoundingClientRect(), which returns a rectangle object with the top, bottom, left and right all relative to the screens viewport. And since the point element has 0 width/height thus top equals to bottom and left equals to right.

clacDistancCornerCenter();

function clacDistancCornerCenter() { var center = document.getElementById('center').getBoundingClientRect(); var corner = document.getElementById('corner').getBoundingClientRect();
//alert("left =" + center.left + "top =" + center.top + "right =" + center.right + "bottom =" + center.bottom);
var centerX = center.left; var centerY = center.top;

var innersCornerX = corner.left; var innersCornerY = corner.top;
//distance formula -> d = sqrt( pow( x2 - x1) + pow( y2 - y1) )
var distance = Math.sqrt(Math.pow(centerX - innersCornerX, 2) + Math.pow(centerY - innersCornerY, 2));
document.getElementById('outRotated').innerHTML = "Distance center to corner: "+ distance;}
function horizontal() {
$('div').css('transform', 'rotate(0deg)');
}

function relocate(top, left) { var point = document.getElementById('corner'); if (top) { point.style.bottom = 'auto'; point.style.top = 0; } else { point.style.top = 'auto'; point.style.bottom = 0;
}
if (left) { point.style.right = 'auto'; point.style.left = 0; } else { point.style.left = 'auto'; point.style.right = 0; } clacDistancCornerCenter();}
div {  top: 15%;  left: 20%;  width: 50%;  height: 50%;  border: 1px solid black;  position: absolute;  display: block;  transform: rotate(25deg);}.point {  width: 7px;  height: 7px;  background-color: blue;  //border:3px solid blue;  position: absolute;  display: block;}#center {  left: 50%;  top: 50%;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><p id="outRotated"></p><p id="outRotated"> Set points width and height to 0 to get the actual correct distance. For now the points are large for easy understanding</p>
<br/><button onclick="relocate(true,true)">Top Left</button>-------<button onclick="relocate(true,false)">Top Right</button><br/><button onclick="relocate(false,true)">Bottom Left</button><button onclick="relocate(false,false)">Bottom Right</button><br/><button onclick="horizontal()">Horizontal</button>
<div id="outer"> <span id="center" class="point" onclick="outputXY()"></span> <div id="mid"> <div id="inner"> <span id="corner" class="point" onclick="outputXY()"></span> </div> </div></div>

Get relative x and y div after rotation

Well, this is how far I got... I made everything smaller so as to see better where the divs end up, and also added scrollbars for this reason.

Important points:

  • Math.sin / Math.cos require radians, not degrees
  • CSS rotates around the midpoint of the element, not (0, 0) (this applies to both the main div and the box divs; first translate -width / 2, -height / 2, rotate, and then translate back)
  • Use parseInt(x, 10) to make sure you're using base 10

Final code: http://jsfiddle.net/pimvdb/dXKJH/10/. This solution needs hardcoded positions and rotations in HTML as well since .css was having some quirks with rotated elements.

$("#div-1").rotate("-10deg");
$("#div-2").rotate("10deg");
$("#div-3").rotate("15deg");
$("#div-4").rotate("75deg");

$("#main").click(function() {
console.log($('body').scrollLeft(), $('body').scrollTop());
});

$("a").click(function(e){
goTo($(this).attr("id").substring(4,5));
return false;
});

function n(x) {
return parseInt(x, 10);
}

function sin(x) {
return Math.sin(x / 180 * Math.PI);
}

function cos(x) {
return Math.cos(x / 180 * Math.PI);
}

function rotate(x, y, a) {
var x2 = cos(a) * x - sin(a) * y;
var y2 = sin(a) * x + cos(a) * y;
return [x2, y2];
}

var offsets = [null,
[0,100,-10],
[100,200, 10],
[300,100, 15],
[400,100, 75]].map(function(v) {
if(!v) return v;
var rotated = rotate(-50, -50, v[2]);
rotated[0] += v[0] + 50;
rotated[1] += v[1] + 50;
return rotated.concat(v[2]);
});

function goTo(num){
var obj = $("#div-" + num);
var angle = -n(obj.rotate());
var pointX = offsets[num][0] - 500;
var pointY = offsets[num][1] - 500;
var rotated = rotate(pointX, pointY, angle);
var newX = rotated[0] + 500;
var newY = rotated[1] + 500;

$("#main").animate({rotate: angle + "deg"}, 1000);
$("body").animate({scrollLeft: newX,
scrollTop: newY}, 1000)
}


Related Topics



Leave a reply



Submit