-
-
Save Yaffle/1145197 to your computer and use it in GitHub Desktop.
/*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> |
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:
BUT you had it working perfectly in an older version of your code as can be seen here:
So I'm not sure what's different specifically but it's probably something small. Great work and thanks again.
thanks,
interesting bug
found it!
[0, element.offsetWidth, 0, element.offsetWidth],
[0, 0, element.offsetHeight, 0, element.offsetHeight], - unwanted zero
Thank you a lot. Very useful indeed.
I used it here: https://github.com/e404/Genesis/blob/master/convertPointFromPageToNode.js
@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/
Thanks for your efforts, is it possible to have it's opposite webkitConvertPointFromNodeToPage javascript alternative version?
@wahjong, yes, I have updated the code to provide convertPointFromNodeToPage
too
I have issue with shadow dom. I can't get point from shadow dom element.
@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
@marchant, hm.... why?
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)?
In line 49 and 50 you are using p4
which does not exist, however, p0
remains unused. Probably just an off-by-one error.
@JannikGM , right, thanks
demo: http://jsfiddle.net/YLCd8/35/