如何制作信息框并附上图表 d3.js

How to make information box and attach it with chart in d3.js

是否可以按照附图中的 d3.js 创建图表?特别是信息框。上下文来自特定的开始和结束日期,如果数据在日期丢失,请在图表中放置一个点而不是条形图。我面临的困难是使用 d3.js.

使用线将信息框与点连接起来

整个图表应使用 (SVG) d3.js 实现。 谁能给出任何数据集的解决方案示例?

这只是一个示例,希望它足以让您入门。

const url = "https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json"

const margin = {
  top: 30,
  left: 40,
  right: 40,
  bottom: 100
};
const width = 800;
const height = 300;

const svg = d3.select("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom);

const g = svg.append('g')
  .attr('transform', `translate(${margin.left}, ${margin.top})`);

const notice = svg.append('g')
  .attr('transform', `translate(${margin.left}, ${margin.top + height})`)

notice.append('rect')
  .classed('notice-box', true)
  .attr('x', 0)
  .attr('y', margin.top)
  .attr('width', width)
  .attr('height', margin.bottom - margin.top);

const warning = notice.append('text')
  .attr('x', 10)
  .attr('y', margin.top + 30);

const format = d3.timeFormat("Q%q %Y")
const setWarning = (data) => {
  warning.text(`Missing data for ${data.map(d => format(d.date)).join(', ')}`);
  notice.selectAll('line')
    .data(data)
    .enter()
    .append('line')
    .attr('stroke', 'black')
    .attr('x1', d => x(d.date))
    .attr('y1', margin.top)
    .attr('x2', d => x(d.date))
    .attr('y2', y(0) - height);

  notice.selectAll('circle')
    .data(data)
    .enter()
    .append('circle')
    .attr('fill', 'black')
    .attr('r', 3)
    .attr('cx', d => x(d.date))
    .attr('cy', y(0) - height);
}

var x = d3.scaleTime().range([0, width]);
const y = d3.scaleLinear().range([height, 0]);

// Since v5 d3.json is promise-based, hence the change.
d3.json(url)
  .then(response => response.data)
  .then(data => data.map(([date, value]) => ({
    date: new Date(date),
    value: value
  })))
  .then(data => {
    data.filter(({
        date
      }) => date.getFullYear() >= 2000 && date.getFullYear() <= 2005 && date.getMonth() === 0)
      .forEach(d => d.value = null);

    x.domain(d3.extent(data.map(({
      date
    }) => date)));
    const barWidth = width / data.length;

    y.domain([0, d3.max(data.map(({
      value
    }) => value))]);

    g.append('g')
      .call(d3.axisBottom().scale(x))
      .attr('id', 'x-axis')
      .attr('transform', `translate(0, ${height})`);

    g.append('g')
      .call(d3.axisLeft(y))
      .attr('id', 'y-axis');

    g.selectAll('rect')
      .data(data)
      .enter()
      .append('rect')
      .attr('class', 'bar')
      .attr('x', d => x(d.date))
      .attr('y', d => y(d.value))
      .attr('width', barWidth)
      .attr('height', d => height - y(d.value))
      .style('fill', '#33adff');

    const missing = data.filter(({
      value
    }) => value === null);
    setWarning(missing);
  });
#y-axis path {
  stroke: black;
  stroke-width: 1;
  fill: none;
}

#x-axis path {
  stroke: black;
  stroke-width: 1;
  fill: none;
}

.notice-box {
  fill: none;
  stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>