Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
347 views
in Technique[技术] by (71.8m points)

d3.js - Need to have two different colors in a linear gradient after a specific (x, y) point

I am trying to achieve this:

enter image description here

with the line path & shadow's transition in sync. And I am able to do that with the help of this answer,

but I am trying to figure out a way to append the red color gradient area after a certain (x, y) point (in this case (48, 0)).

I filtered the graph data and have two arrays (x-axis points -> from 0 to 48 and from 48 to 60), but then don't know how to apply the data in attrTween function.

Here's the stackblitz link.

Here's the shadow function:

    // Blue
    const colorArray1 = [
      ["rgb(8, 141, 218)", "0.8"],
      ["rgb(8, 141, 218)", "0.5"],
      ["rgb(23, 38, 65)", "0.3"]
    ];
    // Red
    const colorArray2 = [
      ["rgb(253, 17, 57)", "0.8"],
      ["rgb(253, 17, 57)", "0.4"],
      ["rgb(23, 38, 65)", "0.3"]
    ];
    const id: string = isShadowBefore48Hr ? "grad1" : "grad2";
    const data: Array<{}> = isShadowBefore48Hr
      ? this.graphData.filter(d => d.hrCount <= 48)
      : this.graphData.filter(d => d.hrCount >= 48);
    const angle: number = isShadowBefore48Hr ? -15 : 0;

    const defs = this.g.append("defs");
    const grad = defs
      .append("linearGradient")
      .attr("id", id)
      .attr("x1", "0%")
      .attr("x2", "0%")
      .attr("y1", "0%")
      .attr("y2", "100%")
      .attr("gradientTransform", "rotate(" + angle + ")");
    grad
      .selectAll("stop")
      .data(isShadowBefore48Hr ? colorArray1 : colorArray2)
      .enter()
      .append("stop")
      .style("stop-color", (d: any) => {
        return d[0];
      })
      .style("stop-opacity", (d: any) => {
        return d[1];
      })
      .attr("offset", (d: any, i: any) => {
        return 100 * (i / 2) + "%";
      });
    const area = d3
      .area()
      .y0(this.height)
      .y1((d: any) => d.y)
      .x((d: any) => d.x);
    this.g
      .append("path")
      .attr("fill", "url(#" + id + ")")
      .transition()
      .duration(5000)
      .attrTween("d", (d: any) => {
        const line = d3.select(".line").node(); // Find the line graph
        const length = line.getTotalLength(); // Get length
        const i = d3.interpolate(0, length); // Interpolator function over length
        const dataPoints = []; // Array to hold accumulated data points
        return (t: any) => {
          dataPoints.push(line.getPointAtLength(i(t))); // On every iteration, get a point on the line
          return area(dataPoints);
        };
      });
  } 

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As suggested in my comment you should have clear two paths (as you can't have only one gradient for both) and maybe reveal the chart by adding a clip path (of the entire chart area) that changes its width from 0 to chart width, essentially showing a chart in transition. This is old code for D3.js version4, slightly adapted with your gradients (you'll figure the rest out):

<html>
<head>
    <meta charset="utf-8" />
<style>
.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 2px;
}

pre#data {
  display: none;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<pre id="data">
date,close
1-May-12,58.13
30-Apr-12,53.98
27-Apr-12,67.00
26-Apr-12,89.70
25-Apr-12,99.00
24-Apr-12,130.28
23-Apr-12,166.70
20-Apr-12,234.98
19-Apr-12,345.44
18-Apr-12,443.34
17-Apr-12,543.70
16-Apr-12,580.13
13-Apr-12,605.23
12-Apr-12,622.77
11-Apr-12,626.20
10-Apr-12,628.44
9-Apr-12,636.23
5-Apr-12,633.68
4-Apr-12,624.31
3-Apr-12,629.32
2-Apr-12,618.63
30-Mar-12,599.55
29-Mar-12,609.86
28-Mar-12,617.62
27-Mar-12,614.48
26-Mar-12,606.98
</pre>
<script>
    let animationDuration = 5000;
    // set the dimensions and margins of the graph
    var margin = {
        top: 20,
        right: 20,
        bottom: 30,
        left: 50
      },
      width = 720 - margin.left - margin.right,
      height = 400 - margin.top - margin.bottom;

    // parse the date / time
    var parseTime = d3.timeParse("%d-%b-%y");

    // set the ranges
    var x = d3.scaleTime().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);

    // define the area
    var area = function(datum, boolean) {
      return d3.area()
        .y0(height)
        .y1(function(d) {
          return boolean ? y(d.close) : y(d.close);
        })
        .x(function(d) {
          return boolean ? x(d.date) : 0;
        })
        (datum);
    }

    // define the line
    var valueline = d3.line()
      .x(function(d) {
        return x(d.date);
      })
      .y(function(d) {
        return y(d.close);
      });

    // append the svg obgect to the body of the page
    // appends a 'group' element to 'svg'
    // moves the 'group' element to the top left margin
    var svg = d3.select("body").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");

    var defs = svg.append("defs");

    var clip = defs.append("clipPath")
      .attr("id", "clip");
    var clipRect = clip.append("rect")
      .attr("width", 0)
      .attr("height", height);

// Blue
  const colorArray1 = [
    ["rgb(8, 141, 218)", "0.8"],
    ["rgb(8, 141, 218)", "0.5"],
    ["rgb(23, 38, 65)", "0.3"]
  ];
  // Red
  const colorArray2 = [
    ["rgb(253, 17, 57)", "0.8"],
    ["rgb(253, 17, 57)", "0.4"],
    ["rgb(23, 38, 65)", "0.3"]
  ];

  function addGradient(isShadowBefore48Hr) {
      const id = isShadowBefore48Hr ? "grad1" : "grad2";
        const angle = isShadowBefore48Hr ? -15 : 0;
        const grad = defs
        .append("linearGradient")
        .attr("id", id)
        .attr("x1", "0%")
        .attr("x2", "0%")
        .attr("y1", "0%")
        .attr("y2", "100%")
            .attr("gradientTransform", "rotate(" + angle + ")");    
        grad
      .selectAll("stop")
      .data(isShadowBefore48Hr ? colorArray1 : colorArray2)
      .enter()
      .append("stop")
      .style("stop-color", (d) => {
        return d[0];
      })
      .style("stop-opacity", (d) => {
        return d[1];
      })
      .attr("offset", (d, i) => {
        return 100 * (i / 2) + "%";
      });
  }

  addGradient(true);
  addGradient(false);

    var data = d3.csvParse(d3.select("pre#data").text());

    data.reverse();

    // format the data
    data.forEach(function(d) {
      d.date = parseTime(d.date);
      d.close = +d.close;
    });

    // scale the range of the data
    x.domain(d3.extent(data, function(d) {
      return d.date;
    }));
    y.domain([0, d3.max(data, function(d) {
      return d.close;
    })]);

    // add first area
    svg.append("path")
      .data([data.filter(function(item) { return item.date <= parseTime("23-Apr-12") } ) ])
      .attr("class", "area")
      .attr("d", d => area(d, true))
      //.attr("fill", "lightsteelblue")
    .attr("fill", "url(#" + "grad1" + ")")
      .attr("clip-path", "url(#clip)");

    // add second area
    svg.append("path")
      .data([data.filter(function(item) { return item.date >= parseTime("23-Apr-12") } ) ])
      .attr("class", "area")
      .attr("d", d => area(d, true))
      //.attr("fill", "red")
        .attr("fill", "url(#" + "grad2" + ")")
      .attr("clip-path", "url(#clip)");

    // add the valueline path.
    svg.append("path")
      .data([data])
      .attr("class", "line")
      .attr("d", valueline)
      .attr("clip-path", "url(#clip)");

    // add the X Axis
/*  svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));
*/
    // add the Y Axis
    svg.append("g")
      .call(d3.axisLeft(y));

    clipRect.transition()
      .duration(5000)
      .ease(d3.easeLinear)
      .attr("width", width)
</script>
</body>
</html>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...