如何获取相对于持有 onclick 监听器的 SVG 元素的点击坐标?

Posted

技术标签:

【中文标题】如何获取相对于持有 onclick 监听器的 SVG 元素的点击坐标?【英文标题】:How to get the click coordinates relative to SVG element holding the onclick listener? 【发布时间】:2015-05-29 10:57:38 【问题描述】:

我无法计算相对于触发事件的元素的点击坐标(x 和 y)。我没有在网上找到一个简单的例子。

我在 html 页面中有一个简单的svg(左边距为 100 像素)。它包含一个group(翻译为30px 30px),它附加了一个onclick 监听器。在 group 里面,我有一个 rect,宽度和高度为 50px。

单击group 元素的任何部分后,我得到一个事件对象,其坐标相对于HTML 页面(evt.clientXevt.clientY)。

我需要知道用户在 group 元素(持有 onclick 侦听器的元素)中点击的确切位置。

如何将clientXclientY 坐标转换为group 元素坐标。所以说,如果我点击rect 的最左上部分,它应该给我 x=0 和 y=0。

这是我目前拥有的:

<!DOCTYPE html>
<html>
    <head>
        <style>
            body
                background:black;
            
            svg
                fill:white;
                background:white;
                position: absolute;
                top:100px;
                left:100px;
            
            
        </style>
        <script>
            function clicked(evt)
                alert("x: "+evt.clientX+" y:"+evt.clientY);
            
        </script>
    </head>
    <body>
        <div>
            <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  >
                <g transform="translate(30 30)" onclick="clicked(evt)">
                    <rect x="0" y="0"   fill="red"/>
                </g>
            </svg>      
        </div>
    </body>
</html>

【问题讨论】:

【参考方案1】:

尝试使用getBoundingClientRect():http://jsfiddle.net/fLo4uatw/

function clicked(evt)
    var e = evt.target;
    var dim = e.getBoundingClientRect();
    var x = evt.clientX - dim.left;
    var y = evt.clientY - dim.top;
    alert("x: "+x+" y:"+y);
  

【讨论】:

请注意,如果您的 viewBox 与默认值 (viewBox="0 0 width height") 不同,此脚本将不起作用。我发布了一个替代答案,它适用于任何 viewBox。【参考方案2】:

Tolokoban 的解决方案有一个限制,即如果您的 viewBox 偏离默认值,即它与viewBox="0 0 width height" 不同,它就不起作用。将 viewBox 考虑在内的更好解决方案是:

var pt = svg.createSVGPoint();  // Created once for document

function alert_coords(evt) 
    pt.x = evt.clientX;
    pt.y = evt.clientY;

    // The cursor point, translated into svg coordinates
    var cursorpt =  pt.matrixTransform(svg.getScreenCTM().inverse());
    console.log("(" + cursorpt.x + ", " + cursorpt.y + ")");

(感谢 Smerk,posted the code)

如果 viewBox 未设置或设置为默认值,此脚本将返回与 Tolokoban 脚本相同的值。但是如果你有一个像&lt;svg width="100px" height="100" viewBox="0 0 200 200"&gt; 这样的SVG,那么只有这个版本会给你正确的结果。

【讨论】:

亚历山大所说的完全正确!我的解决方案特定于发布的示例,其中您是默认视口。 注意 svg 是对 svg 元素的引用。如果使用 react,可以通过添加 ref=(ref) =&gt; this.svg = ref 属性来获取引用。 据我所知,所有支持SVG的浏览器都支持它。根据Microsoft的说法,方法SVGSVGElement.createSVGPoint()是从IE9开始支持的,这也是一般开始支持SVG的时候。 Mozilla 提供了对SVGSVGElement 的支持表,并且由于createSVGPoint() 没有作为单独的功能列出,我认为它属于“基本支持”——这意味着它被所有支持 SVG 的浏览器所支持。跨度> 我关于浏览器支持的陈述可能是错误的。 getScreenCTM()SVGGraphicsElement 的一种方法,根据Mozilla 的说法,IE 不支持它,尽管Microsoft 在他们的网站上提到了它。只需使用支持 SVG 的每个浏览器的最早版本进行测试即可确定。【参考方案3】:

经过多次研究后添加注释(但失败了!)。

对于css翻译的svg,获取点击点的坐标进行绘制。

在我的例子中,使用鼠标滚轮事件来翻译 X,所以实际渲染取决于屏幕大小和实际翻译值。

我建议您为您的用例绘制如下所示的小图,这将有助于弄清楚发生了什么。

假设我的 svg 有 id:shoke 要获得总计算宽度,以像素为单位:

shoke.getBoundingClientRect()["width"]

需要知道实际的 translateX 值。 (从右边开始,所以是负数,在这种情况下)

shoke.style.transform.substr(11).slice(0,-3)

注意它返回的是字符串而不是整数,所以:

+shoke.style.transform.substr(11).slice(0,-3)

现在获取鼠标的坐标,与屏幕的像素 x0 相关

let pt = document.querySelector('svg').createSVGPoint();
pt.matrixTransform(shoke.getScreenCTM().inverse())["x"]

所以,最后,要获得精确的 x 点:

svg_width - (svg_width + translated) + from_pixel x0 of the screen_click

是这样的:

shoke.getBoundingClientRect()["width"]  - (shoke.getBoundingClientRect()["width"] + +shoke.style.transform.substr(11).slice(0,-3)) + pt.matrixTransform(shoke.getScreenCTM().inverse())["x"]

【讨论】:

【参考方案4】:

建议的解决方案很棒,但并非在所有情况下都有效。

OP的帖子标题

如何获取相对于 SVG 元素的点击坐标 按住 onclick 监听器

因此,如果您将 onclick 侦听器放在根 svg 元素上,则无论何时单击其任何子元素,getBoundingClientRect 都会为您提供子元素的 Rect,您将无法获得相对于根svg

这是我的情况,因为我始终需要相对于根的坐标,而对我有用的解决方案是使用 e.target.farthestViewportElement。这是我的(JSX)代码的摘录:

const onClickSvg = e => 
  const  farthestViewportElement: svgRoot  = e.target;
  const dim = svgRoot.getBoundingClientRect();
  const x = e.clientX - dim.left;
  const y = e.clientY - dim.top;
  console.log(`x: $x, y: $y`);
;

<svg onClick=onClickSvg>...</svg>

【讨论】:

以上是关于如何获取相对于持有 onclick 监听器的 SVG 元素的点击坐标?的主要内容,如果未能解决你的问题,请参考以下文章

onClick() 事件适用于一个 div,但不适用于另一个。为啥?

使用 Onclick 侦听器将选定的 ChipGroup 芯片文本放入剪贴板

如何从 onclick 侦听器单击的项目中检索数据

是否可以将 onclick 侦听器添加到 QCheckBox?

将所有 onclick 更改为事件侦听器

如何知道在相对布局中单击了哪个子视图