26

Is it possible to get the four actual corner coordinates of a <div /> that has been transformed with CSS3 attributes like scale, skew and rotate?

Example:

Before the CSS3 transformation the coordinates are

x1y1: 0,0
x1y2: 0,200
x2y1: 200,0
x2yw: 200,200

and the div looks like this:

before transformation

after a little bit of CSS3 magic transform: skew(10deg, 30deg) rotate(30deg) scale(1, 2); it looks like this:

after transformation

How can I get the coordinates (with javascript) of the actual corners (not the bounding box)? Any help greatly appreciated.

Horen
  • 11,184
  • 11
  • 71
  • 113
  • Mostly sure that there's no API for that yet. Remember that CSS transforms operate in the render land while JS operates in the ECMA/DOM land. Though it'd be a nice addiction. – Fabrício Matté Jun 13 '13 at 12:57
  • @FabrícioMatté I guess I could calculate them from the attributes - where are my math friends :) ? – Horen Jun 13 '13 at 13:03
  • Exactly, I've seen a couple of similar questions before and my comment was to apply math. `;)` Though I believe everyone would like to get an API or hack that gives us the correct numbers the lazy way. `:P` – Fabrício Matté Jun 13 '13 at 13:04
  • Brilliant solution! I was racking my head with the math too for too long. this is nice and simple. – Shai UI Mar 17 '15 at 03:41

4 Answers4

26

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()
Horen
  • 11,184
  • 11
  • 71
  • 113
  • 1
    Ha! Brilliant. I've spent hours trying to do the math on this. Finally gave up and looked to SO. Your answer is simple and creative. – Senica Gonzalez Apr 14 '14 at 18:40
  • Just wanted to add that I am currently using this technique and it works perfectly! Why do the work yourself if the browser will do it for you! – Paige DePol Dec 30 '14 at 18:18
  • Note: doesn't work for arbitrary elements. Only works for transformed elements that have style position set explicitly to relative or absolute. Other wise your "handles" could end up rendered outside the bounds of their parent element. – user2782001 Apr 30 '16 at 18:39
  • @user2782001 you could use various methods to make sure each element is at the corner (i.e. a 0x0 container, then the 0x0 with a margin-left or margin-top of the width height <0x0>0x0><0x0>0x0> etc... basically, you can offset the corners using `margin`, but you have contain the corners in an additional 0x0 div. – dansch Jun 13 '17 at 20:31
4

I don't think that there is API for that. As well as there is no API to convert coordinates in HTML5 <canvas>. But there is a way to calculate coordinates manually. Here is a class from my <canvas> library which converts coordinates: https://github.com/enepomnyaschih/jwcanvas/blob/master/jwidget/public/jwcanvas/transform.js

You can use it as a template.

To initialize coordinate system, you should just instantiate an object of JW.Canvas.Transform class and apply method complex. After that, you can apply other transformations to coordinate system via transform method. Matrixes are:

  • translate: [1, 0, 0, 1, x, y]
  • scale: [x, 0, 0, y, 0, 0]
  • rotate clockwise: [cos(a), sin(a), -sin(a), cos(a), 0, 0]
  • skew along x: [1, 0, tan(a), 1, 0, 0]
  • skew along y: [1, tan(a), 0, 1, 0, 0]

After that, you'll be able to convert coordinates via convert method. Use back method to calculate backwards convertion object.

Egor Nepomnyaschih
  • 932
  • 1
  • 8
  • 13
  • Does it work for CSS3 transform3D matrix (rotateX, rotateY and perspective)? – BeauCielBleu Feb 02 '14 at 11:55
  • In 3D, 4x4 matrix is used to represent the transformations, so there will be 12 meaning numbers instead of 6 for orthogonal transformations, with the fixed last line: [0, 0, 0, 1]. Translation, scaling, rotation and skewing transformations will have similar matrixes. Quick googling helps to find a matrix for perspective projection: [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 1/d, 1]] (last line is meaning!) – Egor Nepomnyaschih Feb 03 '14 at 12:48
4

I recommend to you this site : http://www.useragentman.com/blog/2011/01/07/css3-matrix-transform-for-the-mathematically-challenged/ and in particular the section "Other Interesting Facts About Matrices"...

But as Egor Nepomnyaschih pointed out, you just have to implement the calculus for each transformation and to chain them.

I have implemented a jsFiddle based on your example : http://jsfiddle.net/pvS8X/3/ .

Just be careful : the origin is the middle of your figure! If your want to refer to the top, left corner, you have to set this is your CSS code :

transform-origin: 0 0;

cf. https://developer.mozilla.org/fr/docs/CSS/transform-origin.

The main method are these one :

function skew(p, alpha, beta) {
    var tan_a = Math.tan(alpha * Math.PI / 180),
        tan_b = Math.tan(beta * Math.PI / 180),
        p_skewed = {};

    p_skewed.x = p.x + p.y * tan_a;
    p_skewed.y = p.x * tan_b + p.y;

    return p_skewed;
}


function rotate(p, theta) {
    var sin_th = Math.sin(theta * Math.PI / 180),
        cos_th = Math.cos(theta * Math.PI / 180),
        p_rot = {};

    p_rot.x = p.x * cos_th - p.y * sin_th;
    p_rot.y = p.x * sin_th + p.y * cos_th;

    return p_rot;
}


function scale(p, sx, sy) {
    var p_scaled = {};

    p_scaled.x = p.x * sx;
    p_scaled.y = p.y * sy;

    return p_scaled;
}

where p is an object on the form { x: <some_horizontal_pos>, y: <some_vertical_pos>}.

Samuel Caillerie
  • 8,259
  • 1
  • 27
  • 33
-2

You can find the new position of your element respective to the window using .getBoundingClientRect()

Santiago
  • 1,984
  • 4
  • 19
  • 24