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 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