【问题标题】:Mouseout on element grouping fires before leaving bounding area in D3在 D3 中离开边界区域之前,元素分组上的鼠标悬停会触发
【发布时间】:2020-12-31 19:17:15
【问题描述】:

我正在尝试将clickmouseout 处理程序添加到一组圆圈和文本中。点击处理程序工作正常,但即使我没有离开circleGroup 区域(如下所示),鼠标移出似乎也会触发。

注意:这些圆圈组中有​​多个添加到一个网格中。

这是浏览器中显示的 SVG:

生成 circleGroup 的代码如下:

let circleGroup = leftPanelGroup.append('g');

let outerCircle = circleGroup.append("circle")
    .attr("cx", x)
    .attr("cy", y)
    .style('fill', color)
    .attr("r", 15);

let innerCircle = circleGroup.append("circle")
    .attr("cx", x)
    .attr("cy", y)
    .style('fill', color)
    .style('stroke', '#fff')
    .attr("r", 7);

let text = circleGroup.append('text')
    .style('color', '#fff')
    .attr("x", x)
    .attr("y", y - 25)
    .style('fill', '#fff')
    .attr("font-size", 12)
    .attr('font-weight', 'bold')
    .attr("font-family", "sans-serif")
    .attr('id', 'circle-text')
    .style("text-anchor", "middle")
    .text('Thank you');

...

circleGroup 内的任何位置click 上,circleClick 应该会触发。这工作正常。问题是,即使我还没有离开circleGroup 的边界区域,circleMouseout 函数似乎也会随机触发:

circleGroup.on('click', circleClick).on('mouseout', circleMouseout);

function circleClick() {
    // Do something
}
function circleMouseout() {
    // Do something else
}

控制台中的输出 HTML 显示了 <g> svg 组元素的区域。我希望单击此突出显示区域中的任何位置都会触发click 事件,并且只有当我将鼠标移出此突出显示区域时才会触发mouseout 事件。同样,click 工作正常,mouseout 不行。

<g>
   <circle cx="252.99037499999997" cy="340.938" r="15" style="fill: rgb(108, 160, 123);"> 
   </circle>
   <circle cx="252.99037499999997" cy="340.938" r="7" style="fill: rgb(108, 160, 123); stroke: rgb(255, 255, 255);">
   </circle>
   <text x="252.99037499999997" y="315.938" font-size="12" font-weight="bold" font-family="sans-serif" id="circle-text" style="color: rgb(255, 255, 255); fill: rgb(255, 255, 255); text-anchor: middle;">Thank you
   </text>
</g>

【问题讨论】:

  • "问题是,即使我还没有离开 circleGroup 的边界区域,circleMouseout 函数似乎也会随机触发" - 这是预期的行为(嗯,它不应该是随机的,它应该在您将鼠标从圆圈和/或文本上移开时立即出现)。鼠标只与g 中的绘制元素交互(特别是它们的描边和/或填充,如果已定义),而不是g 的边界框。
  • @AndrewReid 所以如果我将事件应用到g,所有包含的元素现在是否都有绑定事件处理程序?
  • 事件处理程序仍在 g 上,但将鼠标悬停在 g 上的唯一方法是将鼠标悬停在子元素上,例如文本或圆形元素。
  • @AndrewReid 谢谢。我不希望mouseout 触发,例如,当鼠标移出较小的包含元素(如textinnerCircle)时。如何防止g 中包含的特定元素触发?这里的用例是我希望在单击时弹出帮助工具提示,并在鼠标退出时淡出。从包含的较小的text 元素中移出鼠标会使用户感到困惑。

标签: javascript svg d3.js events


【解决方案1】:

g 的边界框不会影响鼠标与其交互的位置。鼠标仅与元素的“交互区域”交互。这通常是渲染元素的描边或填充。因此,每当您的鼠标离开圆圈或文本时,都会触发 mouseout 事件,而不是在您离开父 g 的边界框时。

如果你想让父g的边界框与鼠标交互,那么我们需要添加一个新的矩形。我们可以提取g 的bbox 并使用它在g 的边界框上绘制一个新矩形。可以给这个矩形填充none 使其不可见,但也可以给它一个指针事件属性 all 以确保它与鼠标交互:

let boundingBox = circleGroup.append('rect')
  .each(function() {
     var bbox = this.parentNode.getBBox();  // get parent `g` bounding box
     d3.select(this)
       .attr("width", bbox.width)           // size rect based on bounding box.
       .attr("height", bbox.height)
       .attr("x", bbox.x)
       .attr("y", bbox.y)
       .attr("fill","none")
       .attr("pointer-events","all")
  })

现在我们可以将事件侦听器分配给grect,整个g 边界框将响应鼠标事件:

let circleGroup = d3.select("body")
  .append("svg")
  .append("g");

let x = 50;
let y = 50;
let color = "steelblue";

let outerCircle = circleGroup.append("circle")
    .attr("cx", x)
    .attr("cy", y)
    .style('fill', color)
    .attr("r", 15);

let innerCircle = circleGroup.append("circle")
    .attr("cx", x)
    .attr("cy", y)
    .style('fill', color)
    .style('stroke', '#fff')
    .attr("r", 7);

let text = circleGroup.append('text')
    .attr("x", x)
    .attr("y", y - 25)
    .style('fill', color)
    .attr("font-size", 12)
    .attr('font-weight', 'bold')
    .attr("font-family", "sans-serif")
    .attr('id', 'circle-text')
    .style("text-anchor", "middle")
    .text('Thank you');

let boundingBox = circleGroup.append('rect')
  .each(function() {
     var bbox = this.parentNode.getBBox();
     d3.select(this)
       .attr("width", bbox.width)
       .attr("height", bbox.height)
       .attr("x", bbox.x)
       .attr("y", bbox.y)
       .attr("fill","none")
       .attr("pointer-events","all")
  })

circleGroup.on("mouseover", function() {
     console.log("mouseover");
  }).on("mouseout", function() {
     console.log("mouseout");
  })
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"&gt;&lt;/script&gt;

【讨论】:

  • 使用指针事件,不要使用 fill=transparent。
  • @RobertLongson,我知道您是 SVG 的权威,所以我已经更改了答案,您可以快速分享一下为什么指针事件更好的原因吗?
  • 因为它更高效。浏览器有专门的代码,而 fill=transparent 实际上强制更多的渲染代码毫无意义地运行。
猜你喜欢
  • 1970-01-01
  • 2013-07-02
  • 1970-01-01
  • 2021-11-10
  • 1970-01-01
  • 2013-09-30
  • 1970-01-01
  • 2013-02-27
  • 1970-01-01
相关资源
最近更新 更多