d3 折线图,过渡不同的线条颜色

问题描述 投票:0回答:1

我正在使用基于此示例的 d3 折线图: https://codepen.io/louisemoxy/pen/qMvmBM

并且想知道我是否可以根据 y > 0 的条件对这条线(也已经在转换发生时)进行不同的着色。数据可能有多个高于和低于 0 的值,所以它不像将路径分成 2 那么容易碎片。

这是我的更新方法,其中发生转换:

update(data: Data[]): void {
    this.x.domain([new Date(data[0].date), new Date(data[this.data.length - 1].date)])
    this.chart.selectAll('.xaxis')
        .transition()
        .duration(1000)
        .call(this.xAxis);

    const minVal = d3.min(data, (d: Data) => {return d.val}) as number;
    const min = minVal < 0 ? minVal : 0;
    const max = d3.max(data, (d: Data) => { return d.val; }) as number;
    this.y = d3.scaleLinear()
      .domain([min, max])
      .range([ this.height, 0 ]);

    this.yAxis = d3.axisLeft(this.y);

    this.chart.selectAll('.yaxis')
        .transition()
        .duration(1000)
        .call(this.yAxis);

    // Create the u variable
    let u = this.grp.select("path")
        .interrupt()
        .datum(data)
        .attr('class', 'performance')
        .attr('d', d3.line()
            .x((d: Data) => { return this.x(new Date(d.date)); })
            .y((d: Data) => { return this.y(d.val); }));

    const pathLength = u.node().getTotalLength();
    const transitionPath = d3
        .transition()
        .ease(d3.easeSin)
        .duration(1500);
    u
        .attr("stroke-dashoffset", pathLength)
        .attr("stroke-dasharray", pathLength)
        .attr("stroke", "steelblue")
        .transition(transitionPath)
        .attr("stroke-dashoffset", 0);
  }
typescript d3.js linechart
1个回答
0
投票

正如评论指出的,最好的方法是使用

linearGradient
。我打算将其作为重复项关闭,但我能找到的所有答案都没有提供一个很好的工作示例。因此,这是与链接中的示例代码混合在一起的:

<!DOCTYPE html>

<html>
  <head>
    <style>
      #chart {
        text-align: center;
        margin-top: 40px;
      }
    </style>
  </head>

  <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <button>Update Chart</button>
    <div id="chart"></div>
    <script>
      // Fake data designed to get many crossing points at zero
      let data = d3.range(20).map((d) => {
        return {
          year: 2000 + d,
          popularity:
            Math.random() < 0.5 ? Math.random() * -20 : Math.random() * 20,
        };
      });

      // Create SVG and padding for the chart
      const svg = d3
        .select('#chart')
        .append('svg')
        .attr('height', 300)
        .attr('width', 600);

      const defs = svg.append('defs');

      // build the gradient
      const gradient = defs
        .append('linearGradient')
        .attr('id', 'line-gradient')
        .attr('gradientUnits', 'userSpaceOnUse');

      const margin = { top: 0, bottom: 20, left: 30, right: 20 };
      const chart = svg
        .append('g')
        .attr('transform', `translate(${margin.left},0)`);
      const width = +svg.attr('width') - margin.left - margin.right;
      const height = +svg.attr('height') - margin.top - margin.bottom;
      const grp = chart
        .append('g')
        .attr('transform', `translate(-${margin.left},-${margin.top})`);

      // Add empty scales group for the scales to be attatched to on update
      chart.append('g').attr('class', 'x-axis');
      chart.append('g').attr('class', 'y-axis');

      // Add empty path
      const path = grp
        .append('path')
        .attr('transform', `translate(${margin.left},0)`)
        .attr('fill', 'none')
        .attr('stroke', 'url(#line-gradient)')
        .attr('stroke-linejoin', 'round')
        .attr('stroke-linecap', 'round')
        .attr('stroke-width', 1.5);

      function updateScales(data) {
        // Create scales
        const yScale = d3
          .scaleLinear()
          .range([height, 0])
          .domain(d3.extent(data, (dataPoint) => dataPoint.popularity));
        const xScale = d3
          .scaleLinear()
          .range([0, width])
          .domain(d3.extent(data, (dataPoint) => dataPoint.year));

        gradient
          .attr('x1', 0)
          .attr('y1', yScale(yScale.domain()[0]))
          .attr('x2', 0)
          .attr('y2', yScale(0))
          .selectAll('stop')
          .data([
            { offset: '100%', color: 'blue' },
            { offset: '100%', color: 'red' },
          ])
          .enter()
          .append('stop')
          .attr('offset', function (d) {
            return d.offset;
          })
          .attr('stop-color', function (d) {
            return d.color;
          });

        return { yScale, xScale };
      }

      function createLine(xScale, yScale) {
        return (line = d3
          .line()
          .x((dataPoint) => xScale(dataPoint.year))
          .y((dataPoint) => yScale(dataPoint.popularity)));
      }

      function updateAxes(data, chart, xScale, yScale) {
        chart
          .select('.x-axis')
          .attr('transform', `translate(0,${height})`)
          .call(d3.axisBottom(xScale).ticks(data.length));
        chart
          .select('.y-axis')
          .attr('transform', `translate(0, 0)`)
          .call(d3.axisLeft(yScale));
      }

      function updatePath(data, line) {
        const updatedPath = d3
          .select('path')
          .interrupt()
          .datum(data)
          .attr('d', line);

        const pathLength = updatedPath.node().getTotalLength();
        // D3 provides lots of transition options, have a play around here:
        // https://github.com/d3/d3-transition
        const transitionPath = d3.transition().ease(d3.easeSin).duration(2500);
        updatedPath
          .attr('stroke-dashoffset', pathLength)
          .attr('stroke-dasharray', pathLength)
          .transition(transitionPath)
          .attr('stroke-dashoffset', 0);
      }

      function updateChart(data) {
        const { yScale, xScale } = updateScales(data);
        const line = createLine(xScale, yScale);
        updateAxes(data, chart, xScale, yScale);
        updatePath(data, line);
      }

      updateChart(data);
      // Update chart when button is clicked
      d3.select('button').on('click', () => {
        // Create new fake data
        const newData = data.map((row) => {
          return { ...row, popularity: row.popularity * Math.random() };
        });
        updateChart(newData);
      });
    </script>
  </body>
</html>

© www.soinside.com 2019 - 2024. All rights reserved.