在多个重叠的 SVG 元素上检测鼠标事件
Detecting Mouse Events on Multiple Overlapping SVG Elements
我正在尝试检测部分重叠的 SVG 元素上的 mousemove 事件,如图中所示
<svg>
<rect id="red" x=10 y=10 width=60 height=60 style="fill:#ff0000" />
<rect id="orange" x=80 y=10 width=60 height=60 style="fill:#ffcc00" />
<rect id="blue" x=50 y=30 width=60 height=60 style="fill:#0000ff; fill-opacity: 0.8" />
</svg>
$('rect').on('mousemove', function()
{
log(this.id);
});
现在,当将鼠标悬停在 blue/red 交叉点上时,我想检测这两个元素上的鼠标事件,blue/orange 组合也是如此。正如您在日志中看到的那样,在这些情况下,当前仅针对位于顶部的蓝色框触发事件。
这与指针事件有关,因为我可以在 setting the blue element's pointer-events to none 悬停蓝色元素时让红色和橙色元素触发事件。但是后来我没有得到蓝色框的事件,所以这也不是一个可行的选择。
我会使用解决这个问题的库。我查看了 this d3 example 中的事件冒泡,但这只适用于嵌套在 DOM 中的元素。我有很多独立的元素,它们可能与很多其他元素重叠,因此不能以这种方式构建我的 DOM。
我猜最后的手段是找到当前鼠标位置的元素,然后手动触发事件。因此,我查看了document.elementFromPoint(), but that would only yield 1 element (and may not work in SVG?). I found the jQuerypp function within, that finds the elements at a given position, see here. That example looks great, except it's DIVs and not inside SVG. When replacing divs with svg rectangle elements, the fiddle seems to break。
我该怎么办?!
这里的精彩评论给了我答案:通过在光标位置使用 getIntersectionList() 找到它们,可以手动将事件传播到底层元素。
$('svg').on('mousemove', function(evt)
{
var root = $('svg')[0];
var rpos = root.createSVGRect();
rpos.x = evt.clientX;
rpos.y = evt.clientY;
rpos.width = rpos.height = 1;
var list = root.getIntersectionList(rpos, null);
for(var i = 0; i < list.length; i++)
{
if(list[i] != evt.target)
{
$(list[i]).mousemove();
}
}
});
工作示例:http://jsfiddle.net/michaschwab/w0wufbtn/6/
如果其他侦听器需要原始事件对象,请查看 http://jsfiddle.net/michaschwab/w0wufbtn/13/。
非常感谢!!
对于仍在寻找的任何人,elementsFromPoint() returns 鼠标光标下所有元素的节点列表。
注意:还有一个 elementFromPoint()
方法。
当您需要在鼠标悬停时检测多个重叠的 SVG path
元素时,这特别有用。
一个简单的例子:
从鼠标事件中获取节点列表。
const _overlapped = document.elementsFromPoint(e.pageX, e.pageY)
根据某些标准过滤列表:
// Some list of element id's you're interested in
const _lines = ['elId1', 'elId2', 'elId3']
// Check to see if any element id matches an id in _lines
const _included = _overlapped.filter(el => _lines.includes(el.id))
// Perform an action on each member in the list
_included.forEach(...)
我正在尝试检测部分重叠的 SVG 元素上的 mousemove 事件,如图中所示
<svg>
<rect id="red" x=10 y=10 width=60 height=60 style="fill:#ff0000" />
<rect id="orange" x=80 y=10 width=60 height=60 style="fill:#ffcc00" />
<rect id="blue" x=50 y=30 width=60 height=60 style="fill:#0000ff; fill-opacity: 0.8" />
</svg>
$('rect').on('mousemove', function()
{
log(this.id);
});
现在,当将鼠标悬停在 blue/red 交叉点上时,我想检测这两个元素上的鼠标事件,blue/orange 组合也是如此。正如您在日志中看到的那样,在这些情况下,当前仅针对位于顶部的蓝色框触发事件。
这与指针事件有关,因为我可以在 setting the blue element's pointer-events to none 悬停蓝色元素时让红色和橙色元素触发事件。但是后来我没有得到蓝色框的事件,所以这也不是一个可行的选择。
我会使用解决这个问题的库。我查看了 this d3 example 中的事件冒泡,但这只适用于嵌套在 DOM 中的元素。我有很多独立的元素,它们可能与很多其他元素重叠,因此不能以这种方式构建我的 DOM。
我猜最后的手段是找到当前鼠标位置的元素,然后手动触发事件。因此,我查看了document.elementFromPoint(), but that would only yield 1 element (and may not work in SVG?). I found the jQuerypp function within, that finds the elements at a given position, see here. That example looks great, except it's DIVs and not inside SVG. When replacing divs with svg rectangle elements, the fiddle seems to break。
我该怎么办?!
这里的精彩评论给了我答案:通过在光标位置使用 getIntersectionList() 找到它们,可以手动将事件传播到底层元素。
$('svg').on('mousemove', function(evt)
{
var root = $('svg')[0];
var rpos = root.createSVGRect();
rpos.x = evt.clientX;
rpos.y = evt.clientY;
rpos.width = rpos.height = 1;
var list = root.getIntersectionList(rpos, null);
for(var i = 0; i < list.length; i++)
{
if(list[i] != evt.target)
{
$(list[i]).mousemove();
}
}
});
工作示例:http://jsfiddle.net/michaschwab/w0wufbtn/6/
如果其他侦听器需要原始事件对象,请查看 http://jsfiddle.net/michaschwab/w0wufbtn/13/。
非常感谢!!
对于仍在寻找的任何人,elementsFromPoint() returns 鼠标光标下所有元素的节点列表。
注意:还有一个 elementFromPoint()
方法。
当您需要在鼠标悬停时检测多个重叠的 SVG path
元素时,这特别有用。
一个简单的例子:
从鼠标事件中获取节点列表。
const _overlapped = document.elementsFromPoint(e.pageX, e.pageY)
根据某些标准过滤列表:
// Some list of element id's you're interested in
const _lines = ['elId1', 'elId2', 'elId3']
// Check to see if any element id matches an id in _lines
const _included = _overlapped.filter(el => _lines.includes(el.id))
// Perform an action on each member in the list
_included.forEach(...)