使用 D3 更新 SVG 元素 Z-Index

IT技术 javascript svg d3.js
2021-01-18 07:22:38

使用 D3 库将 SVG 元素置于 z 顺序顶部的有效方法是什么?

我的特定情况是一个饼图其中突出(通过添加strokepath)当鼠标在给定的片。生成图表的代码块如下:

svg.selectAll("path")
    .data(d)
  .enter().append("path")
    .attr("d", arc)
    .attr("class", "arc")
    .attr("fill", function(d) { return color(d.name); })
    .attr("stroke", "#fff")
    .attr("stroke-width", 0)
    .on("mouseover", function(d) {
        d3.select(this)
            .attr("stroke-width", 2)
            .classed("top", true);
            //.style("z-index", 1);
    })
    .on("mouseout", function(d) {
        d3.select(this)
            .attr("stroke-width", 0)
            .classed("top", false);
            //.style("z-index", -1);
    });

我已经尝试了一些选择,但到目前为止还没有运气。使用style("z-index")和调用classed两者都不起作用。

“top”类在我的 CSS 中定义如下:

.top {
    fill: red;
    z-index: 100;
}

fill声明是为了确保我知道它正在正确打开/关闭。它是。

我听说 usingsort是一个选项,但我不清楚如何实现将“选定”元素带到顶部。

更新:

我使用以下代码修复了我的特殊情况,该代码在mouseover事件中向SVG 添加了一个新弧以显示突出显示。

svg.selectAll("path")
    .data(d)
  .enter().append("path")
    .attr("d", arc)
    .attr("class", "arc")
    .style("fill", function(d) { return color(d.name); })
    .style("stroke", "#fff")
    .style("stroke-width", 0)
    .on("mouseover", function(d) {
        svg.append("path")
          .attr("d", d3.select(this).attr("d"))
          .attr("id", "arcSelection")
          .style("fill", "none")
          .style("stroke", "#fff")
          .style("stroke-width", 2);
    })
    .on("mouseout", function(d) {
        d3.select("#arcSelection").remove();
    });
6个回答

正如其他答案中所解释的,SVG 没有 z-index 的概念。相反,文档中元素的顺序决定了绘图中的顺序。

除了手动对元素重新排序之外,对于某些情况还有另一种方法:

使用 D3 时,您经常会遇到某些类型的元素,这些元素应该始终绘制在其他类型的元素之上

例如,在布置图形时,链接应始终放置在节点下方。更一般地,一些背景元素通常需要放置在其他所有元素的下方,而一些高光和叠加层应该放置在上方。

如果您遇到这种情况,我发现为这些元素创建父组元素是最好的方法。在 SVG 中,您可以使用该g元素。例如,如果您的链接应始终放置在节点下方,请执行以下操作:

svg.append("g").attr("id", "links")
svg.append("g").attr("id", "nodes")

现在,当您绘制链接和节点时,请按如下方式选择(选择器以#引用元素 id开头):

svg.select("#links").selectAll(".link")
// add data, attach elements and so on

svg.select("#nodes").selectAll(".node")
// add data, attach elements and so on

现在,所有链接将始终在结构上附加在所有节点元素之前。因此,SVG 将显示所有节点下方的所有链接,无论您添加或删除元素的频率和顺序如何。当然,所有相同类型的元素(即在同一个容器内)仍将遵循它们添加的顺序。

惊人的!谢谢你,notan3xit,你救了我的一天!只是为其他人添加一个想法 - 如果您需要以与所需可见性顺序不同的顺序添加元素,您只需以适当的可见性顺序创建组(甚至在向它们添加任何元素之前),然后将元素附加到这些以任何顺序分组。
2021-03-20 07:22:38
这是正确的做法。一想就明白了。
2021-03-20 07:22:38
对我来说, svg.select('#links') ... 必须替换为 d3.select('#links')... 才能工作。也许这会帮助其他人。
2021-04-01 07:22:38
当我真正的问题是将我的元素组织成组时,我遇到了这个想法,我可以将一个项目向上移动。如果您的“真正”问题是移动单个元素,那么上一个答案中描述的排序方法是一个不错的选择。
2021-04-11 07:22:38

开发人员提出的解决方案之一是:“使用 D3 的排序运算符对元素进行重新排序。” (见https://github.com/mbostock/d3/issues/252

有鉴于此,可以通过比较元素的数据或位置(如果它们是无数据元素)来对元素进行排序:

.on("mouseover", function(d) {
    svg.selectAll("path").sort(function (a, b) { // select the parent and sort the path's
      if (a.id != d.id) return -1;               // a is not the hovered element, send "a" to the back
      else return 1;                             // a is the hovered element, bring "a" to the front
  });
})
或者,svg:use元素可以动态地指向凸起的元素。有关解决方案,请参阅stackoverflow.com/a/6289809/292085
2021-03-20 07:22:38
这不仅适用于 Z 顺序,它也不会扰乱任何刚刚开始的元素被提升的拖动。
2021-03-22 07:22:38
对于大量子元素,这将无法充分执行。最好在随后的<g>.
2021-03-26 07:22:38
确实,就像@nkint 一样,它在比较a.id时不起作用d.id解决方法是直接比较ad
2021-04-05 07:22:38
它对我也不起作用。a,b 似乎只是数据,而不是 d3 选择元素或 DOM 元素
2021-04-07 07:22:38

由于 SVG 没有 Z-index 而是使用 DOM 元素的顺序,因此您可以通过以下方式将其置于最前面:

this.parentNode.appendChild(this);

然后,您可以例如使用insertBefore将其放回原处mouseout然而,这要求您能够定位您的元素之前应该插入的兄弟节点。

演示:看看这个JSFiddle

@迈克B。当然,我刚刚更新了我的答案。我意识到 d3 的insert函数可能不是最好的方法,因为它创建了一个元素。我们只想移动一个现有的元素,因此我insertBefore在这个例子中使用
2021-03-21 07:22:38
如果不是 Firefox 无法呈现任何:hover样式,这将是一个很好的解决方案我也不认为这个用例最后需要 ìnsert 函数。
2021-03-25 07:22:38
我尝试使用这种方法,但不幸的是我在 IE11 中不起作用。您有解决此浏览器的方法吗?
2021-03-26 07:22:38
@swenedo,您的解决方案对我很有用,并将元素带到了前面。关于把它放在后面,我有点堆积,不知道如何以正确的方式写它。你能不能,请张贴一个例子,你怎么能把对象带到后面。谢谢。
2021-03-30 07:22:38

SVG 不做 z-index。Z-order 由其容器中 SVG DOM 元素的顺序决定。

据我所知(过去我已经尝试过几次),D3 不提供分离和重新附加单个元素以将其带到前面或诸如此类的方法。

有一个.order()方法,它重新排列节点以匹配它们在选择中出现的顺序。在您的情况下,您需要将单个元素放在前面。因此,从技术上讲,您可以将所需元素放在前面(或在最后,不记得哪个是最上面的),然后调用order()它。

或者,您可以跳过此任务的 d3 并使用普通 JS(或 jQuery)重新插入该单个 DOM 元素。

更改顺序不会也会更改饼图的布局吗?我也在研究 jQuery 以学习如何重新插入元素。
2021-03-14 07:22:38
@EvilClosetMonkey 嗨...这个问题得到了很多观点,我相信下面的未来答案比我的更有帮助。因此,也许可以考虑将接受的答案更改为未来的答案,这样它看起来更高。
2021-03-18 07:22:38
我已经用我选择的解决方案更新了我的问题,这实际上并没有利用原始问题的核心。如果您发现此解决方案有任何不好的地方,欢迎发表评论!感谢您对原始问题的见解。
2021-03-27 07:22:38
似乎是一种合理的方法,主要是因为叠加的形状没有填充。如果是这样,我希望它在鼠标事件出现的那一刻就“窃取”它。而且,我认为@ErikDahlström 的观点仍然成立:这在某些浏览器(主要是 IE9)中仍然可能是一个问题。为了额外的“安全”,您可以添加.style('pointer-events', 'none')到重叠路径中。不幸的是,这个属性在 IE 中没有任何作用,但仍然......
2021-03-30 07:22:38
请注意,在某些浏览器中删除/插入鼠标悬停处理程序中的元素存在问题,这可能导致无法正确发送鼠标悬停事件,因此我建议在所有浏览器中尝试此操作以确保它按预期工作。
2021-04-07 07:22:38

简单的答案是使用 d3 排序方法。除了 d3.select('g').order() 之外,版本 4 中还有.lower().raise()。这会改变元素的显示方式。请查阅文档以获取更多信息 - https://github.com/d3/d3/blob/master/API.md#selections-d3-selection