javascript - d3 raise() 不适用于特定选择

标签 javascript d3.js svg

我有一个用 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() 方法将圆圈一个一个地(使用单一选择和原始元素)放在前面,但它始终无法正常工作。

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

最佳答案

实际上,selection.raise()工作中。这里的问题只是您的 SVG 的树结构:给定行的所有圆圈都属于 <g>元素。

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

Re-inserts each selected element, in order, as the last child of its parent.

上面的强调是我的:这里的关键工作是parent。所以,你想要的是提高 <g>包含所选圆圈的元素 <g>其他圆圈的元素,而不是其内部的圆圈 <g> parent 。

在您的情况下,就像更改...一样简单

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

...到:

myCircles = circles.filter(etc...)

现在,myCircles是带有 <g> 的选择元素,你可以提高。关注filter功能:因为你没有分享你的数据结构,我不知道数据数组是否为 <g>元素(即 this.data )包含 key属性(property)。相应地更改它。

这是一个演示:

每一行都有一组圆圈,每个圆圈都在自己的 <g> 内 parent 。只有左边的圆圈是分开的,所有其他圆圈都是有意一个接一个地画的。当您将鼠标悬停在一个圆圈(使用左侧的圆圈)上时,它的 <g>容器被提升,在这种情况下使用...

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>

关于javascript - d3 raise() 不适用于特定选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53924031/

相关文章:

javascript - 动态 html 内容的自动电话号码检测

python - 如何平滑类似于 d3 的 curveCardinal 方法实现的闭合线的边缘?

typescript - 在 LitElement 项目中使用 @rollup/plugin-image 输出 svgs 以与 RollupJS 捆绑

javascript - 数组中的补丁组动画出错

javascript - 如何使用 PhoneGap 连续播放音频?

javascript - 为什么此代码没有捕获 'error' 事件中的错误?

javascript - 将 SVG 定位在浏览器窗口的中间

macos - d3.js 和破折号 : anyone tried and won?

javascript - html/css/javascript 中的动画线

javascript - 着色器 : Best practice to store them