Skip to content

Instantly share code, notes, and snippets.

@Yaffle
Last active April 30, 2024 03:50
Show Gist options
  • Select an option

  • Save Yaffle/1145197 to your computer and use it in GitHub Desktop.

Select an option

Save Yaffle/1145197 to your computer and use it in GitHub Desktop.
function to get the MouseEvent coordinates for an element that has CSS3 Transforms
/*jslint plusplus: true, vars: true, indent: 2 */
/*
convertPointFromPageToNode(element, event.pageX, event.pageY) -> {x, y}
returns coordinate in element's local coordinate system (works properly with css transforms without perspective projection)
convertPointFromNodeToPage(element, offsetX, offsetY) -> {x, y}
returns coordinate in window's coordinate system (works properly with css transforms without perspective projection)
*/
(function () {
"use strict";
var I = new WebKitCSSMatrix();
function Point(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Point.prototype.transformBy = function (matrix) {
var tmp = matrix.multiply(I.translate(this.x, this.y, this.z));
return new Point(tmp.m41, tmp.m42, tmp.m43);
};
// new WebKitCSSMatrix(), new WebKitCSSMatrix(string)
// WebKitCSSMatrix#m41, WebKitCSSMatrix#m42, WebKitCSSMatrix#m43
// WebKitCSSMatrix#multiply, WebKitCSSMatrix#translate, WebKitCSSMatrix#inverse
function getTransformationMatrix(element) {
var transformationMatrix = I;
var x = element;
while (x != undefined && x !== x.ownerDocument.documentElement) {
var computedStyle = window.getComputedStyle(x, undefined);
var transform = computedStyle.transform || "none";
var c = transform === "none" ? I : new WebKitCSSMatrix(transform);
transformationMatrix = c.multiply(transformationMatrix);
x = x.parentNode;
}
var w = element.offsetWidth;
var h = element.offsetHeight;
var p1 = new Point(0, 0, 0).transformBy(transformationMatrix);
var p2 = new Point(w, 0, 0).transformBy(transformationMatrix);
var p3 = new Point(w, h, 0).transformBy(transformationMatrix);
var p4 = new Point(0, h, 0).transformBy(transformationMatrix);
var left = Math.min(p1.x, p2.x, p3.x, p4.x);
var top = Math.min(p1.y, p2.y, p3.y, p4.y);
var rect = element.getBoundingClientRect();
transformationMatrix = I.translate(window.pageXOffset + rect.left - left, window.pageYOffset + rect.top - top, 0).multiply(transformationMatrix);
return transformationMatrix;
}
window.convertPointFromPageToNode = function (element, pageX, pageY) {
return new Point(pageX, pageY, 0).transformBy(getTransformationMatrix(element).inverse());
};
window.convertPointFromNodeToPage = function (element, offsetX, offsetY) {
return new Point(offsetX, offsetY, 0).transformBy(getTransformationMatrix(element));
};
}());
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>demo</title>
<script src="convertPointFromPageToNode.js"></script>
</head>
<body>
<div id='log' style="position:fixed;right:0;top:0;"></div>
<div style="height: 19em;"></div>
<div style="
-o-transform:translate(130px, -200px) skew(60deg, 0);
-webkit-transform:translate(130px, -200px) skew(60deg, 0);
-ms-transform:translate(130px, -200px) skew(60deg, 0);
-moz-transform:translate(130px, -200px) skew(60deg, 0);
transform:translate(130px, -200px) skew(60deg, 0);
">
<div id="clickMe" style="outline:1px solid silver;
padding: 0px;
-o-transform:rotate(25deg);
-webkit-transform:rotate(25deg);
-ms-transform:rotate(25deg);
-moz-transform:rotate(25deg);
transform:rotate(25deg);
left:0;top:0;
width:120px;
height:70px;
background: ghostwhite;
" >120x70</div>
</div>
<script>
var log = document.getElementById('log');
var element = document.getElementById('clickMe');
function a0(element, pageX, pageY) {
return window.webkitConvertPointFromPageToNode != undefined ? window.webkitConvertPointFromPageToNode(element, new WebKitPoint(pageX, pageY)) : {x: undefined, y: undefined};
}
function a1(element, pageX, pageY) {
return {x: pageX, y: pageY};
}
function a2(element, pageX, pageY) {
return window.convertPointFromPageToNode != undefined ? window.convertPointFromPageToNode(element, pageX, pageY) : {x: undefined, y: undefined};
}
function b0(element, offsetX, offsetY) {
return window.webkitConvertPointFromNodeToPage != undefined ? window.webkitConvertPointFromNodeToPage(element, new WebKitPoint(offsetX, offsetY)) : {x: undefined, y: undefined};
}
function b1(element, offsetX, offsetY) {
return {x: offsetX, y: offsetY};
}
function b2(element, offsetX, offsetY) {
return window.convertPointFromNodeToPage != undefined ? window.convertPointFromNodeToPage(element, offsetX, offsetY) : {x: undefined, y: undefined};
}
function test(name, point) {
return "<div>" +
"<div>" + name + ": " + "</div>" +
"<div>" +
(point.x == undefined ? "undefined" : point.x.toFixed(1)) + ", " +
(point.y == undefined ? "undefined" : point.y.toFixed(1)) +
"</div>" +
"</div>";
}
function onMouseMove(event) {
log.innerHTML = (event.target !== element ? "<div style=\"color: red\">The element should be under the mouse pointer</div>" : "") +
test("offsetX/offsetY", event.target === element ? b1(element, event.offsetX, event.offsetY) : {x: undefined, y: undefined}) +
test("webkitConvertPointFromPageToNode", a0(element, event.pageX, event.pageY)) +
test("convertPointFromPageToNode", a2(element, event.pageX, event.pageY)) +
test("pageX/pageY", a1(element, event.pageX, event.pageY)) +
test("webkitConvertPointFromNodeToPage", event.target === element ? b0(element, event.offsetX, event.offsetY) : {x: undefined, y: undefined}) +
test("convertPointFromNodeToPage", event.target === element ? b2(element, event.offsetX, event.offsetY) : {x: undefined, y: undefined});
}
document.addEventListener("mousemove", onMouseMove, false);
</script>
</body>
</html>
@Yaffle
Copy link
Copy Markdown
Author

Yaffle commented Aug 14, 2011

@Amusesmile
Copy link
Copy Markdown

Hey Yaffle. Thanks for this code- it's been very helpful. I wanted to let you know that there's an error which is causing it to output incorrect values when objects are rotated between 90 and 270 degrees. See this fiddle:

http://jsfiddle.net/eU56R/1/

BUT you had it working perfectly in an older version of your code as can be seen here:

http://jsfiddle.net/7SMak/1/

So I'm not sure what's different specifically but it's probably something small. Great work and thanks again.

@Yaffle
Copy link
Copy Markdown
Author

Yaffle commented Mar 20, 2013

thanks,
interesting bug

@Yaffle
Copy link
Copy Markdown
Author

Yaffle commented Mar 20, 2013

found it!

[0, element.offsetWidth, 0, element.offsetWidth],
      [0, 0, element.offsetHeight, 0, element.offsetHeight], - unwanted zero

@e404
Copy link
Copy Markdown

e404 commented Jun 6, 2014

Thank you a lot. Very useful indeed.
I used it here: https://github.com/e404/Genesis/blob/master/convertPointFromPageToNode.js

@Yaffle
Copy link
Copy Markdown
Author

Yaffle commented Jun 11, 2014

@e404, note: it is better to use new standardized API for this - GeometryUtils.convertPointFromNode, read more at https://hacks.mozilla.org/2014/04/coordinate-conversion-made-easy/

@wahjong
Copy link
Copy Markdown

wahjong commented Oct 12, 2015

Thanks for your efforts, is it possible to have it's opposite webkitConvertPointFromNodeToPage javascript alternative version?

@Yaffle
Copy link
Copy Markdown
Author

Yaffle commented Oct 13, 2015

@wahjong, yes, I have updated the code to provide convertPointFromNodeToPage too

Copy link
Copy Markdown

ghost commented Jul 13, 2016

I have issue with shadow dom. I can't get point from shadow dom element.

@Yaffle
Copy link
Copy Markdown
Author

Yaffle commented Jul 25, 2018

@marchant
Copy link
Copy Markdown

@Yaffle thanks for this, though I had to do this:
var I = new WebKitCSSMatrix([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
//Identity;
I.m11 = 1;
I.m22 = 1;
I.m33 = 1;
I.m44 = 1;

to make it work exactly as former native implementation

@Yaffle
Copy link
Copy Markdown
Author

Yaffle commented Oct 20, 2018

@marchant, hm.... why?

Copy link
Copy Markdown

ghost commented Apr 29, 2022

Is there some ready-to-use npm library for this? Or maybe even some polyfill to implement what was mentioned in https://gist.github.com/Yaffle/1145197?permalink_comment_id=1243925#gistcomment-1243925 (GeometryUtils)?

Copy link
Copy Markdown

ghost commented Apr 29, 2022

In line 49 and 50 you are using p4 which does not exist, however, p0 remains unused. Probably just an off-by-one error.

@Yaffle
Copy link
Copy Markdown
Author

Yaffle commented Jun 23, 2022

@JannikGM , right, thanks

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