如何获得具有CSS3转换的元素的MouseEvent坐标?

时间:2022-10-27 12:43:23

I want to detect where a MouseEvent has occurred, in coordinates relative to the clicked element. Why? Because I want to add an absolutely positioned child element at the clicked location.

我想要检测鼠标事件在哪里发生,在相对于单击的元素的坐标中。为什么?因为我想在单击的位置添加一个绝对定位的子元素。

I know how to detect it when no CSS3 transformations exist (see description below). However, when I add a CSS3 Transform, then my algorithm breaks, and I don't know how to fix it.

我知道在没有CSS3转换时如何检测它(参见下面的描述)。但是,当我添加CSS3转换时,我的算法就会崩溃,我不知道如何修复它。

I'm not using any JavaScript library, and I want to understand how things work in plain JavaScript. So, please, don't answer with "just use jQuery".

我没有使用任何JavaScript库,我想了解在普通JavaScript中是如何工作的。所以,请不要用“jQuery”来回答。

By the way, I want a solution that works for all MouseEvents, not just "click". Not that it matters, because I believe all mouse events share the same properties, thus the same solution should work for all of them.

顺便说一下,我想要一个解决所有鼠标事件的解决方案,而不仅仅是“点击”。这并不重要,因为我认为所有的鼠标事件都具有相同的属性,因此相同的解决方案应该适用于所有的这些属性。


Background information

According to DOM Level 2 specification, a MouseEvent has few properties related to getting the event coordinates:

根据DOM Level 2规范,MouseEvent几乎没有与事件坐标相关的属性:

  • screenX and screenY return the screen coordinates (the origin is the top-left corner of user's monitor)
  • screenX和screenY返回屏幕坐标(原点是用户监视器的左上角)
  • clientX and clientY return the coordinates relative the document viewport.
  • clientX和clientY返回相对于文档viewport的坐标。

Thus, in order to find the position of the MouseEvent relative to the clicked element content, I must do this math:

因此,为了找到鼠标事件相对于单击的元素内容的位置,我必须做这个数学:

ev.clientX - this.getBoundingClientRect().left - this.clientLeft + this.scrollLeft
  • ev.clientX is the coordinate relative to the document viewport
  • 电动汽车。clientX是相对于文档viewport的坐标。
  • this.getBoundingClientRect().left is the position of the element relative to the document viewport
  • this.getBoundingClientRect()。左边是相对于文档视图端口的元素的位置。
  • this.clientLeft is the amount of border (and scrollbar) between the element boundary and the inner coordinates
  • 这一点。clientLeft是元素边界和内部坐标之间的边框(和滚动条)的数量。
  • this.scrollLeft is the amount of scrolling inside the element
  • 这一点。scrollLeft是元素内滚动的量。

getBoundingClientRect(), clientLeft and scrollLeft are specified at CSSOM View Module.

getBoundingClientRect()、clientLeft和scrollLeft是在c森视图模块中指定的。

Experiment without CSS Transform (it works)

Confusing? Try the following piece of JavaScript and HTML. Upon clicking, a red dot should appear exactly where the click has happened. This version is "quite simple" and works as expected.

困惑吗?尝试下面的JavaScript和HTML。点击后,一个红点应该出现在点击发生的地方。这个版本“相当简单”,并按预期工作。

function click_handler(ev) {
    var rect = this.getBoundingClientRect();
    var left = ev.clientX - rect.left - this.clientLeft + this.scrollLeft;
    var top = ev.clientY - rect.top - this.clientTop + this.scrollTop;

    var dot = document.createElement('div');
    dot.setAttribute('style', 'position:absolute; width: 2px; height: 2px; top: '+top+'px; left: '+left+'px; background: red;');
    this.appendChild(dot);
}

document.getElementById("experiment").addEventListener('click', click_handler, false);

<div id="experiment" style="border: 5px inset #AAA; background: #CCC; height: 400px; position: relative; overflow: auto;">
    <div style="width: 900px; height: 2px;"></div> 
    <div style="height: 900px; width: 2px;"></div>
</div>

Experiment adding a CSS Transform (it fails)

Now, try adding a CSS transform:

现在,尝试添加一个CSS转换:

#experiment {
    transform: scale(0.5);
    -moz-transform: scale(0.5);
    -o-transform: scale(0.5);
    -webkit-transform: scale(0.5);
    /* Note that this is a very simple transformation. */
    /* Remember to also think about more complex ones, as described below. */
}

The algorithm doesn't know about the transformations, and thus calculates a wrong position. What's more, the results are different between Firefox 3.6 and Chrome 12. Opera 11.50 behaves just like Chrome.

该算法不知道转换,因此计算错误的位置。更重要的是,Firefox 3.6和Chrome 12的结果是不同的。Opera 11.50的表现就像Chrome。

In this example, the only transformation was scaling, so I could multiply the scaling factor to calculate the correct coordinate. However, if we think about arbitrary transformations (scale, rotate, skew, translate, matrix), and even nested transformations (a transformed element inside another transformed element), then we really need a better way to calculate the coordinates.

在这个例子中,唯一的变换是缩放,所以我可以用比例因子来计算正确的坐标。但是,如果我们考虑任意的转换(缩放、旋转、倾斜、转换、矩阵),甚至是嵌套转换(在另一个转换元素内的转换元素),那么我们确实需要一个更好的方法来计算坐标。

11 个解决方案

#1


14  

The behaviour you are experiencing is correct, and your algorithm isn't breaking. Firstly CSS3 Transforms are designed not to interfere with the box model.

你所经历的行为是正确的,你的算法并没有崩溃。首先,CSS3变换的设计不影响盒模型。

To try and explain...

试图解释……

When you apply a CSS3 Transform on an element. the Element assumes a kind of relative positioning. In that the surrounding elements are not effected by the transformed element.

当你在一个元素上应用CSS3变换时。该元素假定一种相对定位。在这种情况下,周围的元素不受转换元素的影响。

e.g. imagine three div's in a horizontal row. If you apply a scale transform to decrease the size of the centre div. The surrounding div's will not move inwards to occupy the space that was once occupied the transformed element.

想象三个div在一个水平线。如果您使用一个缩放变换来减小中心div的大小,那么周围的div将不会向内移动,以占用转换后的元素所占用的空间。

example: http://jsfiddle.net/AshMokhberi/bWwkC/

例如:http://jsfiddle.net/AshMokhberi/bWwkC/

So in the box model, the element does not actually change size. Only it's rendered size changes.

在框模型中,元素实际上并没有改变大小。只有它呈现了大小的变化。

You also have to keep in mind that you are applying a scale Transform, so your elements "real" size is actually the same as it's original size. You are only changing it's perceived size.

您还必须记住,您正在应用一个缩放变换,所以您的元素“真实”的大小实际上与它原来的大小是一样的。你只是在改变它的感知大小。

To explain..

解释. .

Imagine you create a div with a width of 1000px and scale it down to 1/2 the size. The internal size of the div is still 1000px, not 500px.

假设您创建了一个宽度为1000px的div,并将它的大小缩小到1/2。div的内部大小仍然是1000px,而不是500px。

So the position of your dots are correct relative to the div's "real" size.

因此,相对于div的“真实”大小,你的点的位置是正确的。

I modified your example to illustrate.

我修改了你的例子来说明。

Instructions

指令

  1. Click the div and keep you mouse in the same position.
  2. 单击div并将鼠标置于相同位置。
  3. Find the dot in the wrong position.
  4. 找出错误位置的点。
  5. Press Q, the div will become the correct size.
  6. 按Q, div将会变成正确的大小。
  7. Move your mouse to find the dot in the correct position to where you clicked.
  8. 移动你的鼠标,在正确的位置找到你点击的点。

http://jsfiddle.net/AshMokhberi/EwQLX/

http://jsfiddle.net/AshMokhberi/EwQLX/

So in order to make the mouse clicks co-ordinates match the visible location on the div, you need to understand that the mouse is giving back co-ordinates based on the window, and your div offsets are also based on its "real" size.

因此,为了使鼠标点击坐标与div中的可见位置匹配,您需要了解鼠标是基于窗口返回坐标的,您的div偏移也基于它的“真实”大小。

As your object size is relative to the window the only solution is to scale the offset co-ordinates by the same scale value as your div.

由于您的对象大小相对于窗口,惟一的解决方案是使用与您的div相同的刻度值来缩放偏移坐标。

However this can get tricky based on where you set the Transform-origin property of your div. As that is going to effect the offsets.

然而,基于您设置的div的转换源属性,这可能会变得棘手,因为这会影响到偏移量。

See here.

在这里看到的。

http://jsfiddle.net/AshMokhberi/KmDxj/

http://jsfiddle.net/AshMokhberi/KmDxj/

Hope this helps.

希望这个有帮助。

#2


8  

if element is container and positioned absolute or relative, you can place inside of it element, position it relative to parent and width = 1px, height = 1px, and move to inside of container, and after each move use document.elementFromPoint(event.clientX, event.clientY) =))))

如果元素是容器,定位绝对或相对,您可以放置在it元素的内部,位置相对于父元素和宽度= 1px,高度= 1px,移动到容器内部,每个移动使用document。elementfrompoint(事件)。clientX event.clientY)=))))

You can use binary search to make it faster. looks terrible, but it works

你可以用二分查找来加快速度。看起来很糟糕,但是很有效。

http://jsfiddle.net/3VT5N/3/ - demo

http://jsfiddle.net/3VT5N/3/演示

#3


3  

Also, for Webkit webkitConvertPointFromPageToNode method can be used:

另外,Webkit webkitConvertPointFromPageToNode方法可以使用:

var div = document.createElement('div'), scale, point;
div.style.cssText = 'position:absolute;left:-1000px;top:-1000px';
document.body.appendChild(div);
scale = webkitConvertPointFromNodeToPage(div, new WebKitPoint(0, 0));
div.parentNode.removeChild(div);
scale.x = -scale.x / 1000;
scale.y = -scale.y / 1000;
point = webkitConvertPointFromPageToNode(element, new WebKitPoint(event.pageX * scale.x, event.pageY * scale.y));
point.x = point.x / scale.x;
point.y = point.y / scale.x;

#4


2  

To get the coordinates of a MouseEvent relative to the clicked element, use offsetX / layerX.

要获得鼠标事件相对于单击的元素的坐标,请使用offsetX / layerX。

Have you tried using ev.layerX or ev.offsetX?

你试过使用电动汽车吗?layerX还是ev.offsetX ?

var offsetX = (typeof ev.offsetX == "number") ? ev.offsetX : ev.layerX || 0;

See also:

参见:

#5


2  

BY FAR the fastest. The accepted answer takes about 40-70 ms on my 3d transforms site, this usually takes less than 20 (fiddle):

迄今为止最快的。在我的3d变换网站上,接受的答案大约是40-70毫秒,这通常要少于20(小提琴):

function getOffset(event,elt){
    var st=new Date().getTime();
    var iterations=0;
    //if we have webkit, then use webkitConvertPointFromPageToNode instead
    if(webkitConvertPointFromPageToNode){
        var webkitPoint=webkitConvertPointFromPageToNode(elt,new WebKitPoint(event.clientX,event.clientY));
        //if it is off-element, return null
        if(webkitPoint.x<0||webkitPoint.y<0)
            return null;
        return {
            x: webkitPoint.x,
            y: webkitPoint.y,
            time: new Date().getTime()-st
        }
    }
    //make full-size element on top of specified element
    var cover=document.createElement('div');
    //add styling
    cover.style.cssText='height:100%;width:100%;opacity:0;position:absolute;z-index:5000;';
    //and add it to the document
    elt.appendChild(cover);
    //make sure the event is in the element given
    if(document.elementFromPoint(event.clientX,event.clientY)!==cover){
        //remove the cover
        cover.parentNode.removeChild(cover);
        //we've got nothing to show, so return null
        return null;
    }
    //array of all places for rects
    var rectPlaces=['topleft','topcenter','topright','centerleft','centercenter','centerright','bottomleft','bottomcenter','bottomright'];
    //function that adds 9 rects to element
    function addChildren(elt){
        iterations++;
        //loop through all places for rects
        rectPlaces.forEach(function(curRect){
            //create the element for this rect
            var curElt=document.createElement('div');
            //add class and id
            curElt.setAttribute('class','offsetrect');
            curElt.setAttribute('id',curRect+'offset');
            //add it to element
            elt.appendChild(curElt);
        });
        //get the element form point and its styling
        var eltFromPoint=document.elementFromPoint(event.clientX,event.clientY);
        var eltFromPointStyle=getComputedStyle(eltFromPoint);
        //Either return the element smaller than 1 pixel that the event was in, or recurse until we do find it, and return the result of the recursement
        return Math.max(parseFloat(eltFromPointStyle.getPropertyValue('height')),parseFloat(eltFromPointStyle.getPropertyValue('width')))<=1?eltFromPoint:addChildren(eltFromPoint);
    }
    //this is the innermost element
    var correctElt=addChildren(cover);
    //find the element's top and left value by going through all of its parents and adding up the values, as top and left are relative to the parent but we want relative to teh wall
    for(var curElt=correctElt,correctTop=0,correctLeft=0;curElt!==cover;curElt=curElt.parentNode){
        //get the style for the current element
        var curEltStyle=getComputedStyle(curElt);
        //add the top and left for the current element to the total
        correctTop+=parseFloat(curEltStyle.getPropertyValue('top'));
        correctLeft+=parseFloat(curEltStyle.getPropertyValue('left'));
    }
    //remove all of the elements used for testing
    cover.parentNode.removeChild(cover);
    //the returned object
    var returnObj={
        x: correctLeft,
        y: correctTop,
        time: new Date().getTime()-st,
        iterations: iterations
    }
    return returnObj;
}

and also include the following CSS in the same page:

并在同一页面中包含以下CSS:

.offsetrect{
    position: absolute;
    opacity: 0;
    height: 33.333%;
    width: 33.333%;
}
#topleftoffset{
    top: 0;
    left: 0;
}
#topcenteroffset{
    top: 0;
    left: 33.333%;
}
#toprightoffset{
    top: 0;
    left: 66.666%;
}
#centerleftoffset{
    top: 33.333%;
    left: 0;
}
#centercenteroffset{
    top: 33.333%;
    left: 33.333%;
}
#centerrightoffset{
    top: 33.333%;
    left: 66.666%;
}
#bottomleftoffset{
    top: 66.666%;
    left: 0;
}
#bottomcenteroffset{
    top: 66.666%;
    left: 33.333%;
}
#bottomrightoffset{
    top: 66.666%;
    left: 66.666%;
}

It essentially splits the element into 9 squares, determines which one the click was in via document.elementFromPoint. It then splits that into 9 smaller squares, etc until it is accurate to within a pixel. I know I over-commented it. The accepted answer is several times slower than this.

它将元素分割成9个方块,通过document.elementFromPoint来确定单击的位置。然后把它分成9个小方块,直到它精确到一个像素。我知道我说得太多了。被接受的答案要比这个慢好几倍。

EDIT: It is now even faster, and if the user is in Chrome or Safari it will use a native function designed for this instead of the 9 sectors thingy and can do it consistently in LESS THAN 2 MILLISECONDS!

编辑:现在的速度更快了,如果用户是在Chrome或Safari浏览器中,它将使用一个本地功能,而不是9个扇区,并且可以在不到2毫秒的时间内完成。

#6


1  

another way is place 3 divs in corners of that element, than find transform matrix ... but is also works only for positioned containerable elements – 4esn0k

另一种方法是在元素的角落放置3个div,而不是找到变换矩阵…但也只能用于定位的可装载的元素- 4esn0k。

demo: http://jsfiddle.net/dAwfF/3/

演示:http://jsfiddle.net/dAwfF/3/

#7


1  

This seems to work really well for me

这对我来说似乎很有效。

var elementNewXPosition = (event.offsetX != null) ? event.offsetX : event.originalEvent.layerX;
var elementNewYPosition = (event.offsetY != null) ? event.offsetY : event.originalEvent.layerY; 

#8


0  

Works fine whether relative or absolute :) simple solution

var p = $( '.divName' );
var position = p.position();  
var left = (position.left  / 0.5);
var top =  (position.top  / 0.5);

#9


0  

I am working on a polyfill to transfrom DOM coordinates. The GeometryUtils api is not available yet (@see https://drafts.csswg.org/cssom-view/). I created a "simple" code in 2014 to transform coordinates, like localToGlobal, globalToLocal and localToLocal. Its not finished yet, but its working :) I think I will finish it in the coming months (05.07.2017), so if you still need a API to accomplish coordinate transformation give it a try: https://github.com/jsidea/jsidea jsidea core library. Its not stable yet (pre alpha). You can use it like that:

我正在处理从DOM坐标转换到的polyfill。目前还不能使用几何yutils api (@see https://drafts.csswg.org/cssom-view/)。我在2014年创建了一个“简单”代码来转换坐标,比如localToGlobal、globalToLocal和localToLocal。它还没有完成,但是它的工作:)我想我将在接下来的几个月里完成它(05.07.2017),所以如果你还需要一个API来完成坐标转换,那就试试:https://github.com/jsidea/jsidea jsidea core library。它还不稳定(pre - alpha)。你可以这样使用:

Create your transform instance:

创建您的转换实例:

var transformer = jsidea.geom.Transform.create(yourElement);

The box model you want to transform to (default:"border", will be replaced by ENUM's later on):

您想要转换成的框模型(默认为:“边框”,将在稍后的ENUM中被替换):

var toBoxModel = "border";

The box model where your input coordinates coming from (default:"border"):

输入坐标来自(默认为“边框”)的框模型:

var fromBoxModel = "border";

Transform your global coordinates (here {x:50, y:100, z: 0}) to local space. The resulting point has 4 components: x, y, z and w.

转换您的全局坐标(这里{x:50, y:100, z: 0})到本地空间。得到的点有4个分量:x、y、z和w。

var local = transformer.globalToLocal(50, 100, 0, toBoxModel, fromBoxModel);

I have implemented some other functions like localToGlobal and localToLocal. If you want to give a try, just download the release build and use the jsidea.min.js.

我已经实现了一些其他功能,比如localToGlobal和localToLocal。如果您想尝试一下,只需下载版本构建并使用jsidea.min.js。

Download the first release here: Download TypeScript code

下载第一个版本:下载TypeScript代码。

Feel free to change the code, I never put it under any license :)

您可以随意更改代码,我从未将其置于任何许可之下:)

#10


0  

I have this issue and started trying to compute the matrix. I started a library around it: https://github.com/ombr/referentiel

我有这个问题,开始尝试计算矩阵。我在它周围创建了一个库:https://github.com/ombr/referentiel。

$('.referentiel').each ->
  ref = new Referentiel(this)
  $(this).on 'click', (e)->
    input = [e.pageX, e.pageY]
    p = ref.global_to_local(input)
    $pointer = $('.pointer', this)
    $pointer.css('left', p[0])
    $pointer.css('top', p[1])

What do you think ?

你觉得怎么样?

#11


-1  

EDIT: my answer is untested, WIP, I will update when I get it working.

编辑:我的答案是未经测试的,WIP,当我工作的时候我会更新。

I'm implementing a polyfill of the geomtetry-interfaces. The DOMPoint.matrixTransform method I will make next, which means we should be able to write something like the following in order to map a click coordinate onto a transformed (possiblly nested) DOM element:

我正在实现一个多填充的土工界面。DOMPoint。我接下来要做的matrixTransform方法,这意味着我们应该能够编写类似如下的东西,以便将单击坐标映射到转换的(可能嵌套的)DOM元素:

// target is the element nested somewhere inside the scene.
function multiply(target) {
    let result = new DOMMatrix;

    while (target && /* insert your criteria for knowing when you arrive at the root node of the 3D scene*/) {
        const m = new DOMMatrix(target.style.transform)
        result.preMultiplySelf(m) // see w3c DOMMatrix (geometry-interfaces)
        target = target.parentNode
    }

    return result
}

// inside click handler
// traverse from nested node to root node and multiply on the way
const matrix = multiply(node)
const relativePoint = DOMPoint(clickX, clickY, 0, 800).matrixTransform(matrix)

relativePoint will be the point relative to the element's surface that you clicked on.

相对于你点击的元素表面的相对点。

A w3c DOMMatrix can be constructed with a CSS transform string argument, which makes it super easy to use in JavaScript.

可以用CSS转换字符串参数构造一个w3c DOMMatrix,这使得它在JavaScript中非常容易使用。

Unfortunately, this isn't working yet (only Firefox has a geometry-interfaces implementation, and my polyfill does not yet accept a CSS transform string). See: https://github.com/trusktr/geometry-interfaces/blob/7872f1f78a44e6384529e22505e6ca0ba9b30a4d/src/DOMMatrix.js#L15-L18

不幸的是,这还不起作用(只有Firefox有一个几何接口实现,而我的polyfill还没有接受CSS转换字符串)。见:https://github.com/trusktr/geometry-interfaces/blob/7872f1f78a44e6384529e22505e6ca0ba9b30a4d/src/DOMMatrix.js L15-L18

I will update this once I implement that and have a working example. Pull requests welcome!

一旦我实现了这个,我将更新这个,并有一个工作示例。把请求欢迎!

EDIT: the value 800 is the scene's perspective, I'm not sure if this is what the fourth value for the DOMPoint constructor should be when we intend to do something like this. Also, I'm not sure if I should use preMultiplySelf or postMultiplySelf. I'll find out once I get it at least working (values may be incorrect at first) and will update my answer.

编辑:值800是场景的透视图,我不确定这是否是DOMPoint构造函数的第四个值,当我们打算这样做的时候。另外,我不确定我是否应该使用preMultiplySelf或postMultiplySelf。我将发现一旦我得到它至少工作(值可能是错误的一开始)并且将更新我的答案。

#1


14  

The behaviour you are experiencing is correct, and your algorithm isn't breaking. Firstly CSS3 Transforms are designed not to interfere with the box model.

你所经历的行为是正确的,你的算法并没有崩溃。首先,CSS3变换的设计不影响盒模型。

To try and explain...

试图解释……

When you apply a CSS3 Transform on an element. the Element assumes a kind of relative positioning. In that the surrounding elements are not effected by the transformed element.

当你在一个元素上应用CSS3变换时。该元素假定一种相对定位。在这种情况下,周围的元素不受转换元素的影响。

e.g. imagine three div's in a horizontal row. If you apply a scale transform to decrease the size of the centre div. The surrounding div's will not move inwards to occupy the space that was once occupied the transformed element.

想象三个div在一个水平线。如果您使用一个缩放变换来减小中心div的大小,那么周围的div将不会向内移动,以占用转换后的元素所占用的空间。

example: http://jsfiddle.net/AshMokhberi/bWwkC/

例如:http://jsfiddle.net/AshMokhberi/bWwkC/

So in the box model, the element does not actually change size. Only it's rendered size changes.

在框模型中,元素实际上并没有改变大小。只有它呈现了大小的变化。

You also have to keep in mind that you are applying a scale Transform, so your elements "real" size is actually the same as it's original size. You are only changing it's perceived size.

您还必须记住,您正在应用一个缩放变换,所以您的元素“真实”的大小实际上与它原来的大小是一样的。你只是在改变它的感知大小。

To explain..

解释. .

Imagine you create a div with a width of 1000px and scale it down to 1/2 the size. The internal size of the div is still 1000px, not 500px.

假设您创建了一个宽度为1000px的div,并将它的大小缩小到1/2。div的内部大小仍然是1000px,而不是500px。

So the position of your dots are correct relative to the div's "real" size.

因此,相对于div的“真实”大小,你的点的位置是正确的。

I modified your example to illustrate.

我修改了你的例子来说明。

Instructions

指令

  1. Click the div and keep you mouse in the same position.
  2. 单击div并将鼠标置于相同位置。
  3. Find the dot in the wrong position.
  4. 找出错误位置的点。
  5. Press Q, the div will become the correct size.
  6. 按Q, div将会变成正确的大小。
  7. Move your mouse to find the dot in the correct position to where you clicked.
  8. 移动你的鼠标,在正确的位置找到你点击的点。

http://jsfiddle.net/AshMokhberi/EwQLX/

http://jsfiddle.net/AshMokhberi/EwQLX/

So in order to make the mouse clicks co-ordinates match the visible location on the div, you need to understand that the mouse is giving back co-ordinates based on the window, and your div offsets are also based on its "real" size.

因此,为了使鼠标点击坐标与div中的可见位置匹配,您需要了解鼠标是基于窗口返回坐标的,您的div偏移也基于它的“真实”大小。

As your object size is relative to the window the only solution is to scale the offset co-ordinates by the same scale value as your div.

由于您的对象大小相对于窗口,惟一的解决方案是使用与您的div相同的刻度值来缩放偏移坐标。

However this can get tricky based on where you set the Transform-origin property of your div. As that is going to effect the offsets.

然而,基于您设置的div的转换源属性,这可能会变得棘手,因为这会影响到偏移量。

See here.

在这里看到的。

http://jsfiddle.net/AshMokhberi/KmDxj/

http://jsfiddle.net/AshMokhberi/KmDxj/

Hope this helps.

希望这个有帮助。

#2


8  

if element is container and positioned absolute or relative, you can place inside of it element, position it relative to parent and width = 1px, height = 1px, and move to inside of container, and after each move use document.elementFromPoint(event.clientX, event.clientY) =))))

如果元素是容器,定位绝对或相对,您可以放置在it元素的内部,位置相对于父元素和宽度= 1px,高度= 1px,移动到容器内部,每个移动使用document。elementfrompoint(事件)。clientX event.clientY)=))))

You can use binary search to make it faster. looks terrible, but it works

你可以用二分查找来加快速度。看起来很糟糕,但是很有效。

http://jsfiddle.net/3VT5N/3/ - demo

http://jsfiddle.net/3VT5N/3/演示

#3


3  

Also, for Webkit webkitConvertPointFromPageToNode method can be used:

另外,Webkit webkitConvertPointFromPageToNode方法可以使用:

var div = document.createElement('div'), scale, point;
div.style.cssText = 'position:absolute;left:-1000px;top:-1000px';
document.body.appendChild(div);
scale = webkitConvertPointFromNodeToPage(div, new WebKitPoint(0, 0));
div.parentNode.removeChild(div);
scale.x = -scale.x / 1000;
scale.y = -scale.y / 1000;
point = webkitConvertPointFromPageToNode(element, new WebKitPoint(event.pageX * scale.x, event.pageY * scale.y));
point.x = point.x / scale.x;
point.y = point.y / scale.x;

#4


2  

To get the coordinates of a MouseEvent relative to the clicked element, use offsetX / layerX.

要获得鼠标事件相对于单击的元素的坐标,请使用offsetX / layerX。

Have you tried using ev.layerX or ev.offsetX?

你试过使用电动汽车吗?layerX还是ev.offsetX ?

var offsetX = (typeof ev.offsetX == "number") ? ev.offsetX : ev.layerX || 0;

See also:

参见:

#5


2  

BY FAR the fastest. The accepted answer takes about 40-70 ms on my 3d transforms site, this usually takes less than 20 (fiddle):

迄今为止最快的。在我的3d变换网站上,接受的答案大约是40-70毫秒,这通常要少于20(小提琴):

function getOffset(event,elt){
    var st=new Date().getTime();
    var iterations=0;
    //if we have webkit, then use webkitConvertPointFromPageToNode instead
    if(webkitConvertPointFromPageToNode){
        var webkitPoint=webkitConvertPointFromPageToNode(elt,new WebKitPoint(event.clientX,event.clientY));
        //if it is off-element, return null
        if(webkitPoint.x<0||webkitPoint.y<0)
            return null;
        return {
            x: webkitPoint.x,
            y: webkitPoint.y,
            time: new Date().getTime()-st
        }
    }
    //make full-size element on top of specified element
    var cover=document.createElement('div');
    //add styling
    cover.style.cssText='height:100%;width:100%;opacity:0;position:absolute;z-index:5000;';
    //and add it to the document
    elt.appendChild(cover);
    //make sure the event is in the element given
    if(document.elementFromPoint(event.clientX,event.clientY)!==cover){
        //remove the cover
        cover.parentNode.removeChild(cover);
        //we've got nothing to show, so return null
        return null;
    }
    //array of all places for rects
    var rectPlaces=['topleft','topcenter','topright','centerleft','centercenter','centerright','bottomleft','bottomcenter','bottomright'];
    //function that adds 9 rects to element
    function addChildren(elt){
        iterations++;
        //loop through all places for rects
        rectPlaces.forEach(function(curRect){
            //create the element for this rect
            var curElt=document.createElement('div');
            //add class and id
            curElt.setAttribute('class','offsetrect');
            curElt.setAttribute('id',curRect+'offset');
            //add it to element
            elt.appendChild(curElt);
        });
        //get the element form point and its styling
        var eltFromPoint=document.elementFromPoint(event.clientX,event.clientY);
        var eltFromPointStyle=getComputedStyle(eltFromPoint);
        //Either return the element smaller than 1 pixel that the event was in, or recurse until we do find it, and return the result of the recursement
        return Math.max(parseFloat(eltFromPointStyle.getPropertyValue('height')),parseFloat(eltFromPointStyle.getPropertyValue('width')))<=1?eltFromPoint:addChildren(eltFromPoint);
    }
    //this is the innermost element
    var correctElt=addChildren(cover);
    //find the element's top and left value by going through all of its parents and adding up the values, as top and left are relative to the parent but we want relative to teh wall
    for(var curElt=correctElt,correctTop=0,correctLeft=0;curElt!==cover;curElt=curElt.parentNode){
        //get the style for the current element
        var curEltStyle=getComputedStyle(curElt);
        //add the top and left for the current element to the total
        correctTop+=parseFloat(curEltStyle.getPropertyValue('top'));
        correctLeft+=parseFloat(curEltStyle.getPropertyValue('left'));
    }
    //remove all of the elements used for testing
    cover.parentNode.removeChild(cover);
    //the returned object
    var returnObj={
        x: correctLeft,
        y: correctTop,
        time: new Date().getTime()-st,
        iterations: iterations
    }
    return returnObj;
}

and also include the following CSS in the same page:

并在同一页面中包含以下CSS:

.offsetrect{
    position: absolute;
    opacity: 0;
    height: 33.333%;
    width: 33.333%;
}
#topleftoffset{
    top: 0;
    left: 0;
}
#topcenteroffset{
    top: 0;
    left: 33.333%;
}
#toprightoffset{
    top: 0;
    left: 66.666%;
}
#centerleftoffset{
    top: 33.333%;
    left: 0;
}
#centercenteroffset{
    top: 33.333%;
    left: 33.333%;
}
#centerrightoffset{
    top: 33.333%;
    left: 66.666%;
}
#bottomleftoffset{
    top: 66.666%;
    left: 0;
}
#bottomcenteroffset{
    top: 66.666%;
    left: 33.333%;
}
#bottomrightoffset{
    top: 66.666%;
    left: 66.666%;
}

It essentially splits the element into 9 squares, determines which one the click was in via document.elementFromPoint. It then splits that into 9 smaller squares, etc until it is accurate to within a pixel. I know I over-commented it. The accepted answer is several times slower than this.

它将元素分割成9个方块,通过document.elementFromPoint来确定单击的位置。然后把它分成9个小方块,直到它精确到一个像素。我知道我说得太多了。被接受的答案要比这个慢好几倍。

EDIT: It is now even faster, and if the user is in Chrome or Safari it will use a native function designed for this instead of the 9 sectors thingy and can do it consistently in LESS THAN 2 MILLISECONDS!

编辑:现在的速度更快了,如果用户是在Chrome或Safari浏览器中,它将使用一个本地功能,而不是9个扇区,并且可以在不到2毫秒的时间内完成。

#6


1  

another way is place 3 divs in corners of that element, than find transform matrix ... but is also works only for positioned containerable elements – 4esn0k

另一种方法是在元素的角落放置3个div,而不是找到变换矩阵…但也只能用于定位的可装载的元素- 4esn0k。

demo: http://jsfiddle.net/dAwfF/3/

演示:http://jsfiddle.net/dAwfF/3/

#7


1  

This seems to work really well for me

这对我来说似乎很有效。

var elementNewXPosition = (event.offsetX != null) ? event.offsetX : event.originalEvent.layerX;
var elementNewYPosition = (event.offsetY != null) ? event.offsetY : event.originalEvent.layerY; 

#8


0  

Works fine whether relative or absolute :) simple solution

var p = $( '.divName' );
var position = p.position();  
var left = (position.left  / 0.5);
var top =  (position.top  / 0.5);

#9


0  

I am working on a polyfill to transfrom DOM coordinates. The GeometryUtils api is not available yet (@see https://drafts.csswg.org/cssom-view/). I created a "simple" code in 2014 to transform coordinates, like localToGlobal, globalToLocal and localToLocal. Its not finished yet, but its working :) I think I will finish it in the coming months (05.07.2017), so if you still need a API to accomplish coordinate transformation give it a try: https://github.com/jsidea/jsidea jsidea core library. Its not stable yet (pre alpha). You can use it like that:

我正在处理从DOM坐标转换到的polyfill。目前还不能使用几何yutils api (@see https://drafts.csswg.org/cssom-view/)。我在2014年创建了一个“简单”代码来转换坐标,比如localToGlobal、globalToLocal和localToLocal。它还没有完成,但是它的工作:)我想我将在接下来的几个月里完成它(05.07.2017),所以如果你还需要一个API来完成坐标转换,那就试试:https://github.com/jsidea/jsidea jsidea core library。它还不稳定(pre - alpha)。你可以这样使用:

Create your transform instance:

创建您的转换实例:

var transformer = jsidea.geom.Transform.create(yourElement);

The box model you want to transform to (default:"border", will be replaced by ENUM's later on):

您想要转换成的框模型(默认为:“边框”,将在稍后的ENUM中被替换):

var toBoxModel = "border";

The box model where your input coordinates coming from (default:"border"):

输入坐标来自(默认为“边框”)的框模型:

var fromBoxModel = "border";

Transform your global coordinates (here {x:50, y:100, z: 0}) to local space. The resulting point has 4 components: x, y, z and w.

转换您的全局坐标(这里{x:50, y:100, z: 0})到本地空间。得到的点有4个分量:x、y、z和w。

var local = transformer.globalToLocal(50, 100, 0, toBoxModel, fromBoxModel);

I have implemented some other functions like localToGlobal and localToLocal. If you want to give a try, just download the release build and use the jsidea.min.js.

我已经实现了一些其他功能,比如localToGlobal和localToLocal。如果您想尝试一下,只需下载版本构建并使用jsidea.min.js。

Download the first release here: Download TypeScript code

下载第一个版本:下载TypeScript代码。

Feel free to change the code, I never put it under any license :)

您可以随意更改代码,我从未将其置于任何许可之下:)

#10


0  

I have this issue and started trying to compute the matrix. I started a library around it: https://github.com/ombr/referentiel

我有这个问题,开始尝试计算矩阵。我在它周围创建了一个库:https://github.com/ombr/referentiel。

$('.referentiel').each ->
  ref = new Referentiel(this)
  $(this).on 'click', (e)->
    input = [e.pageX, e.pageY]
    p = ref.global_to_local(input)
    $pointer = $('.pointer', this)
    $pointer.css('left', p[0])
    $pointer.css('top', p[1])

What do you think ?

你觉得怎么样?

#11


-1  

EDIT: my answer is untested, WIP, I will update when I get it working.

编辑:我的答案是未经测试的,WIP,当我工作的时候我会更新。

I'm implementing a polyfill of the geomtetry-interfaces. The DOMPoint.matrixTransform method I will make next, which means we should be able to write something like the following in order to map a click coordinate onto a transformed (possiblly nested) DOM element:

我正在实现一个多填充的土工界面。DOMPoint。我接下来要做的matrixTransform方法,这意味着我们应该能够编写类似如下的东西,以便将单击坐标映射到转换的(可能嵌套的)DOM元素:

// target is the element nested somewhere inside the scene.
function multiply(target) {
    let result = new DOMMatrix;

    while (target && /* insert your criteria for knowing when you arrive at the root node of the 3D scene*/) {
        const m = new DOMMatrix(target.style.transform)
        result.preMultiplySelf(m) // see w3c DOMMatrix (geometry-interfaces)
        target = target.parentNode
    }

    return result
}

// inside click handler
// traverse from nested node to root node and multiply on the way
const matrix = multiply(node)
const relativePoint = DOMPoint(clickX, clickY, 0, 800).matrixTransform(matrix)

relativePoint will be the point relative to the element's surface that you clicked on.

相对于你点击的元素表面的相对点。

A w3c DOMMatrix can be constructed with a CSS transform string argument, which makes it super easy to use in JavaScript.

可以用CSS转换字符串参数构造一个w3c DOMMatrix,这使得它在JavaScript中非常容易使用。

Unfortunately, this isn't working yet (only Firefox has a geometry-interfaces implementation, and my polyfill does not yet accept a CSS transform string). See: https://github.com/trusktr/geometry-interfaces/blob/7872f1f78a44e6384529e22505e6ca0ba9b30a4d/src/DOMMatrix.js#L15-L18

不幸的是,这还不起作用(只有Firefox有一个几何接口实现,而我的polyfill还没有接受CSS转换字符串)。见:https://github.com/trusktr/geometry-interfaces/blob/7872f1f78a44e6384529e22505e6ca0ba9b30a4d/src/DOMMatrix.js L15-L18

I will update this once I implement that and have a working example. Pull requests welcome!

一旦我实现了这个,我将更新这个,并有一个工作示例。把请求欢迎!

EDIT: the value 800 is the scene's perspective, I'm not sure if this is what the fourth value for the DOMPoint constructor should be when we intend to do something like this. Also, I'm not sure if I should use preMultiplySelf or postMultiplySelf. I'll find out once I get it at least working (values may be incorrect at first) and will update my answer.

编辑:值800是场景的透视图,我不确定这是否是DOMPoint构造函数的第四个值,当我们打算这样做的时候。另外,我不确定我是否应该使用preMultiplySelf或postMultiplySelf。我将发现一旦我得到它至少工作(值可能是错误的一开始)并且将更新我的答案。