【问题标题】:d3 raise() not working on specific selectiond3 raise() 不适用于特定选择
【发布时间】:2018-12-30 23:39:21
【问题描述】:

我有一个用d3 制作的折线图,但由于数据的形状,线和点(我在每个特定数据点的线上使用点)通常最终会相互重叠.

为了解决这个问题,我结束了对线条和点的不透明度0.4,当您将鼠标悬停在一条线上时,该特定数据行的线条和点会弹出,并将其不透明度设置为1 .

我的问题是:我正在使用 .raise() 函数使它们弹出并站在其余的线条和点上,该功能仅适用于我的线条选择而不是我的点选择,我不知道为什么。

我的代码:

// draw the data lines
    const lines = svg.selectAll('.line')
      .data(this.data)
      .enter()
      .append('path')
      .attr('class', 'data.line')
      .attr("fill", "none")
      .attr("stroke", d => colors(d.key))
      .attr("stroke-linejoin", "round")
      .attr("stroke-linecap", "round")
      .attr("stroke-width", 2.5)
      .attr('stroke-opacity', 0.4)
      .attr('d', d => line(d.values))
      .on('mouseenter', d => {
        // Highlight them
        let myCircles = circles.selectAll('.circle');
        lines.attr('stroke-opacity', b => {
          return b.key === d.key ? 1 : 0.4;
        });
        myCircles.attr('fill-opacity', b => {
          return b[this.typeIdentifier] === d.key ? 1 : 0.4;
        });
        // Bring them to the front
        myCircles = circles.selectAll('.circle')
          .filter(b => b[this.typeIdentifier] === d.key);
        const myLines = lines.filter(b => b.key === d.key);
        myLines.raise();
        myCircles.raise();
      });

// draw the circles
    const circles = svg.selectAll('.circle')
      .data(this.data)
      .enter()
      .append('g');

    circles.selectAll('.circle')
      .data(d => d.values)
      .enter()
      .append('circle')
      .attr('class', 'circle')
      .attr('stroke', 'white')
      .attr('stroke-width', 1)
      .attr('r', 6)
      .attr('fill', d => colors(d[this.typeIdentifier]))
      .attr('fill-opacity', 0.4)
      .attr('cx', d => x(d[this.xAxisValue]) + x.bandwidth() / 2)
      .attr('cy', d => y(d[this.yAxisValue]))
      .on('mouseenter', (d, b, j) => {
        tooltip.raise();
        tooltip.style("display", null);
        tooltip.select("#text1").text(d[this.typeIdentifier])
          .attr('fill', colors(d[this.typeIdentifier]));
        tooltip.select('#text4').text(d[this.yAxisValue]);
        tooltip.select('#text5').text(d[this.xAxisValue]);
        const tWidth = tooltip.select('#text1').node().getComputedTextLength() > 60 ? tooltip.select('#text1').node().getComputedTextLength() + 20 : 80;
        tooltipRect.attr('width', tWidth);
        const xPosition = d3.mouse(j[b])[0];
        const yPosition = d3.mouse(j[b])[1];
        if (xPosition + tWidth + 35 < this.xWIDTH) {  // display on the right
          tooltip.attr("transform", `translate(${xPosition + 15}, ${yPosition - 25})`);
        } else {  // display on the left
          tooltip.attr("transform", `translate(${xPosition - tWidth - 15}, ${yPosition - 25})`);
        }
      })
      .on('mouseleave', d => {
        tooltip.style("display", "none");
      })

因此,当您将鼠标悬停在一条线上时,这应该会将与其关联的线和点放在前面,不透明度为1,但由于某种原因,它仅适用于lines 选择,并且不在myCircles 选择中。选择不是空的,我一直在打印它们来测试它。此外,我尝试使用 .raise() 方法将圆圈一个一个(带有单一选择和原始元素)带到前面,但它不起作用。

为什么它不起作用?是否与将鼠标悬停在圆圈上的工具提示有关?我做错了什么而没有看到吗?

【问题讨论】:

    标签: javascript d3.js svg


    【解决方案1】:

    实际上,selection.raise() 正在工作。这里的问题只是 SVG 的树结构:给定行的所有圆圈都属于 &lt;g&gt; 元素。

    如果你look at the docs,你会看到selection.raise()

    按顺序重新插入每个选定元素,作为其父级的最后一个子级。

    上面的重点是我的:这里的关键工作是parent。因此,您想要的是将包含所选圆圈的 &lt;g&gt; 元素提升到其他圆圈的其他 &lt;g&gt; 元素之上,而不是它们的 &lt;g&gt; 父级中的圆圈。

    在你的情况下,它就像改变一样简单......

    myCircles = circles.selectAll('.circle').filter(etc...)
    

    ...到:

    myCircles = circles.filter(etc...)
    

    现在,myCircles 是带有 &lt;g&gt; 元素的选择,您可以提出它。注意filter函数:因为你没有共享你的数据结构我不知道&lt;g&gt;元素的数据数组(即this.data)是否包含key属性。相应地更改它。

    这是一个演示:

    我们为每一行设置了一组圆圈,每个圆圈都包含在自己的 &lt;g&gt; 父级中。只有左边的圆圈是分开的,所有其他的圆圈都是故意画的。当您将鼠标悬停在一个圆圈上(使用左侧的圆圈)时,它的&lt;g&gt; 容器会升起,在这种情况下使用...

    d3.select(this.parentNode).raise()
    

    ...,所以所有的圆圈都是可见的:

    const svg = d3.select("svg");
    const scale = d3.scaleOrdinal(d3.schemeSet1);
    const lineGenerator = d3.line()
      .x(function(d) {
        return d.x
      })
      .y(function(d) {
        return d.y
      })
    const data = d3.range(5).map(function(d) {
      return {
        key: d,
        values: d3.range(5).map(function(e) {
          return {
            x: 50 + 100 * e,
            y: e ? 150 : 50 + 50 * d
          }
        })
      }
    });
    const lines = svg.selectAll(null)
      .data(data)
      .enter()
      .append("path")
      .attr("d", function(d) {
        return lineGenerator(d.values);
      })
      .style("fill", "none")
      .style("stroke-width", "3px")
      .style("stroke", function(d) {
        return scale(d.key)
      });
    const circleGroups = svg.selectAll(null)
      .data(data)
      .enter()
      .append("g");
    const circles = circleGroups.selectAll(null)
      .data(function(d) {
        return d.values
      })
      .enter()
      .append("circle")
      .attr("r", 20)
      .attr("cx", function(d) {
        return d.x
      })
      .attr("cy", function(d) {
        return d.y
      })
      .style("fill", function(d) {
        return scale(d3.select(this.parentNode).datum().key)
      });
    circles.on("mouseover", function(d) {
      const thisKey = d3.select(this.parentNode).datum().key;
      lines.filter(function(e) {
        return e.key === thisKey;
      }).raise();
      d3.select(this.parentNode).raise();
    })
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <svg width="500" height="300"></svg>

    【讨论】:

    • 谢谢!非常好的解释,它对我的​​问题帮助很大。我想我的问题完全是没有注意元素被重新插入作为其父级的最后一个子级的事实。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2017-09-12
    • 2014-11-26
    • 1970-01-01
    • 1970-01-01
    • 2021-07-03
    • 1970-01-01
    • 2021-05-13
    • 1970-01-01
    相关资源
    最近更新 更多