在区域图 d3 中的路径末尾附加一条线

Append a line to an end of a path in area chart d3

我正在尝试将一条线附加到面积图路径的末尾。我所在领域最困难的部分是动画。我有一个 clipPath,其宽度从 width: 0 转换为 width: 960,末尾的行也随之同步,因此应该同步。此外,该行顶部的文本需要在其进行时进行更新。

期望的输出:

我最初的想法是建立一个图表区域并添加一个 clipPath,然后在面积图中添加一个条形图,这样我就可以根据附加的条形更新我的文本,但是条形图不在我的面积图中。在面积图中放置条形图我做错了什么,或者有更好的解决方案吗?

// Area chart width and height
const width1 = 1000,
  height1 = 100;

// Define x and y scale for area chart
const xScale1 = d3.scaleTime().range([0, width1]);
const yScale1 = d3.scaleLinear().range([height1, 0]);

// Define x and y range for bar chart
let xScale2 = d3.scaleBand().range([0, width1]);
let yScale2 = d3.scaleLinear().range([height1, 0]);

// Add SVG to #areachart
const svg1 = d3
  .select('#areachart')
  .append('svg')
  .attr('viewBox', `0 0 ${width1} ${height1}`)
  .attr('transform', 'translate(' + 0 + ',' + -50 + ')');

const g1 = svg1.append('g');

// Fetch data
d3.json(
    'https://api.coronavirus.data.gov.uk/v1/data?filters=areaName=United%2520Kingdom;areaType=overview&structure=%7B%22areaType%22:%22areaType%22,%22areaName%22:%22areaName%22,%22areaCode%22:%22areaCode%22,%22date%22:%22date%22,%22newCasesByPublishDate%22:%22newCasesByPublishDate%22,%22cumCasesByPublishDate%22:%22cumCasesByPublishDate%22%7D&format=json'
  )
  .then(function(data) {
    console.log('DATES SLICED ----->', data.data.slice(30, 281));

    //Define xScale1 & yScale1 domain after data loaded
    yScale1.domain([
      0,
      d3.max(data.data, function(d) {
        return +d.cumCasesByPublishDate;
      }),
    ]);

    xScale1.domain(
      d3.extent(data.data, function(d) {
        return new Date(d.date);
      })
    );

    // Area  generator
    const area = d3
      .area()
      .curve(d3.curveStepAfter)
      .x((d) => xScale1(new Date(d.date)))
      .y1((d) => yScale1(+d.cumCasesByPublishDate))
      .y0(yScale1(0));

    g1.append('path')
      .datum(data.data.slice(30, 200))
      .attr('d', area)
      .classed('placeholder-layer', true)
      .style('fill', '#dadada')
      .style('opacity', '0.3');

    // clipPath for areachart fill animation
    const clip = g1.append('clipPath').attr('id', 'clip');
    const clipRect = clip.append('rect').attr('width', 0).attr('height', 750);

    g1.append('path')
      .datum(data.data.slice(30, 200))
      .attr('d', area)
      .attr('clip-path', 'url(#clip)')
      .classed('overlay-layer', true)
      .style('fill', 'yellow')
      .style('opacity', '0.3');

    g1.append('line').attr('stroke-width', 960).style('stroke', 'yellow');

    clipRect
      .transition()
      .duration(10000)
      .ease(d3.easeLinear)
      .attr('width', 960);

    //x and y domain for bar chart
    xScale2.domain(data.data.slice(30, 200).map((d) => new Date(d.date)));
    yScale2.domain([
      0,
      d3.max(data.data, function(d) {
        return +d.cumCasesByPublishDate;
      }),
    ]);

    g1.selectAll('rect')
      .data(data.data.slice(30, 200))
      .enter()
      .append('rect')
      .style('fill', 'red')
      .attr('width', xScale2.bandwidth() * 10)
      .attr('height', (d) => yScale2(+d.cumCasesByPublishDate))
      .attr('x', 0)
      .attr('y', function(d) {
        return yScale2(+d.cumCasesByPublishDate);
      })
      .transition()
      .delay(function(d, i) {
        return i * 30;
      })
      .attr('x', function(d) {
        return xScale2(new Date(d.date));
      })
      .duration(100);
  })
  // if there's an error, log it
  .catch((error) => console.log(error));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<section id="map">
  <div id="visualisation-container">
    <div id="visualisation"></div>
    <div id="areachart"></div>
  </div>
</section>

我只使用一条线,没有横条,然后使用 transition.tween with d3.interpolateDate 来更改文本。

// Area chart width and height
const width1 = 800,
  height1 = 250,
  marginBottom = 50

// Define x and y scale for area chart
const xScale1 = d3.scaleTime().range([0, width1]);
const yScale1 = d3.scaleLinear().range([height1, marginBottom]);

// Define x and y range for bar chart
let xScale2 = d3.scaleBand().range([0, width1]);
let yScale2 = d3.scaleLinear().range([height1, marginBottom]);

// Add SVG to #areachart
const svg1 = d3
  .select('#areachart')
  .append('svg')
  .attr('width', width1)
  .attr('height', height1);

const g1 = svg1.append('g');

// Fetch data
d3.json(
    'https://api.coronavirus.data.gov.uk/v1/data?filters=areaName=United%2520Kingdom;areaType=overview&structure=%7B%22areaType%22:%22areaType%22,%22areaName%22:%22areaName%22,%22areaCode%22:%22areaCode%22,%22date%22:%22date%22,%22newCasesByPublishDate%22:%22newCasesByPublishDate%22,%22cumCasesByPublishDate%22:%22cumCasesByPublishDate%22%7D&format=json'
  )
  .then(data => {
    data.data.forEach(d => {
      d.cumCasesByPublishDate = +d.cumCasesByPublishDate;
      d.date = new Date(d.date);
    });
    return data.data.slice(30, 200);
  })
  .then(function(data) {
    //Define xScale1 & yScale1 domain after data loaded
    yScale1.domain([
      0,
      d3.max(data, d => d.cumCasesByPublishDate),
    ]);

    xScale1.domain(
      d3.extent(data, d => d.date)
    );

    // Area  generator
    const area = d3
      .area()
      .curve(d3.curveStepAfter)
      .x((d) => xScale1(d.date))
      .y1((d) => yScale1(d.cumCasesByPublishDate))
      .y0(yScale1(0));

    g1.append('path')
      .datum(data)
      .attr('d', area)
      .classed('placeholder-layer', true)
      .style('fill', '#dadada')
      .style('opacity', '0.3');

    // clipPath for areachart fill animation
    const clip = g1.append('clipPath').attr('id', 'clip');
    const clipRect = clip.append('rect').attr('width', 0).attr('height', 750);

    g1.append('path')
      .datum(data)
      .attr('d', area)
      .attr('clip-path', 'url(#clip)')
      .classed('overlay-layer', true)
      .style('fill', 'yellow')
      .style('opacity', '0.3');

    const format = d3.timeFormat("%B %d, %Y");
    const duration = 10000;
    g1.append('line')
      .attr('stroke-width', 5)
      .style('stroke', 'black')
      .attr('x1', xScale1.range()[0])
      .attr('x2', xScale1.range()[0])
      .attr('y1', yScale1.range()[0])
      .attr('y2', yScale1.range()[1])
      .transition()
      .duration(duration)
      .ease(d3.easeLinear)
      .attr('x1', xScale1.range()[1])
      .attr('x2', xScale1.range()[1])

    g1.append('text')
      .attr('x', xScale1.range()[0])
      .attr('y', marginBottom / 2)
      .attr('text-anchor', 'middle')
      .transition()
      .duration(duration)
      .ease(d3.easeLinear)
      .attr('x', xScale1.range()[1])
      .tween('text', function() {
        const i = d3.interpolateDate(xScale1.domain()[0], xScale1.domain()[1]);
        return (t) => d3.select(this).text(format(i(t)));
      })
  })
  // if there's an error, log it
  .catch((error) => console.log(error));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<section id="map">
  <div id="visualisation-container">
    <div id="visualisation"></div>
    <div id="areachart"></div>
  </div>
</section>