You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在D3.js折线图的路径段之间添加间隙和文本?

D3.js折线图:为路径添加间隙及中间文本

我已经用D3.js创建了一个折线图,现在希望在折线的各路径段之间添加间隙,并在间隙位置显示文本。

当前实现代码

HTML

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Stacked Bar and Line Chart</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
  </head>
  <style>
    .dot {
  display: none;
}

.line-segment {
  fill: none;
  stroke: steelblue;
  stroke-width: 2;
}

.line-text {
  font-size: 12px;
  font-weight: bold;
}
  </style>
  <body>
    <div id="main-chart">
        <svg width="960" height="500"></svg>
    </div>
  </body>
</html>

JavaScript

const data = [
  { year: "2015", value: 120 },
  { year: "2016", value: 200 },
  { year: "2017", value: 300 },
  { year: "2018", value: 400 },
  { year: "2019", value: 500 }
];

const margin = { top: 20, right: 20, bottom: 30, left: 50 };
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;

const 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})`);

const xScale = d3.scaleBand()
  .range([0, width])
  .domain(data.map(d => d.year))
  .padding(0.2);

const yScale = d3.scaleLinear()
  .range([height, 0])
  .domain([0, d3.max(data, d => d.value)]);

const line = d3.line()
  .x(d => xScale(d.year) + xScale.bandwidth() / 2)
  .y(d => yScale(d.value))
  .defined(function(d) {return !isNaN(d)});

svg.append("g")
  .attr("transform", `translate(0, ${height})`)
  .call(d3.axisBottom(xScale));

svg.append("g")
  .call(d3.axisLeft(yScale));

svg.selectAll(".line-segment")
  .data(data.slice(0, -1))
  .enter().append("path")
  .attr("class", "line-segment")
  .attr("d", d => {
    const x1 = xScale(d.year) + xScale.bandwidth() / 2;
    const x2 = xScale(data[data.indexOf(d) + 1].year) + xScale.bandwidth() / 2;
    const y1 = yScale(d.value);
    const y2 = yScale(data[data.indexOf(d) + 1].value);
    return `M${x1},${y1} L${x2},${y2}`;
  })
  .attr("stroke", "steelblue")
  .attr("stroke-width", 2);


svg.selectAll(".line-text")
  .data(data)
  .enter().append("text")
  .attr("class", "line-text")
  .text(d => d.value)
  .attr("x", d => xScale(d.year) + xScale.bandwidth() / 2)
  .attr("y", d => yScale(d.value) - 0)
  .attr("text-anchor", "middle")
  .attr("font-size", "12px");

效果对比

当前效果

当前折线图效果

期望效果

期望的折线图效果

修改方案

要实现期望的间隙和中间文本效果,需要对路径段进行截断处理,并在间隙位置添加文本,具体修改如下:

1. 为路径段添加间隙

通过计算线段长度,将每个路径段的起点和终点向中间缩进固定距离,形成间隙。替换原有的.line-segment生成代码:

svg.selectAll(".line-segment")
  .data(data.slice(0, -1))
  .enter().append("path")
  .attr("class", "line-segment")
  .attr("d", (d, i) => {
    const nextData = data[i + 1];
    // 原始起点和终点坐标
    const x1 = xScale(d.year) + xScale.bandwidth() / 2;
    const y1 = yScale(d.value);
    const x2 = xScale(nextData.year) + xScale.bandwidth() / 2;
    const y2 = yScale(nextData.value);
    
    // 计算线段长度与缩进比例
    const dx = x2 - x1;
    const dy = y2 - y1;
    const segmentLength = Math.sqrt(dx * dx + dy * dy);
    const gapSize = 10; // 可自定义间隙大小
    const scaleRatio = (segmentLength - gapSize) / segmentLength;
    
    // 计算缩进后的新起点和终点
    const newStartX = x1 + dx * (1 - scaleRatio) / 2;
    const newStartY = y1 + dy * (1 - scaleRatio) / 2;
    const newEndX = x2 - dx * (1 - scaleRatio) / 2;
    const newEndY = y2 - dy * (1 - scaleRatio) / 2;
    
    return `M${newStartX},${newStartY} L${newEndX},${newEndY}`;
  })
  .attr("stroke", "steelblue")
  .attr("stroke-width", 2);

2. 在间隙处添加文本

在每两个数据点的中间位置(间隙处)添加文本,示例中显示数值差值,可根据需求修改文本内容:

svg.selectAll(".gap-text")
  .data(data.slice(0, -1))
  .enter().append("text")
  .attr("class", "line-text")
  .text((d, i) => `+${data[i + 1].value - d.value}`) // 显示数值增量
  .attr("x", (d, i) => {
    const x1 = xScale(d.year) + xScale.bandwidth() / 2;
    const x2 = xScale(data[i + 1].year) + xScale.bandwidth() / 2;
    return (x1 + x2) / 2; // 水平居中
  })
  .attr("y", (d, i) => {
    const y1 = yScale(d.value);
    const y2 = yScale(data[i + 1].value);
    return (y1 + y2) / 2 - 8; // 垂直居中并向上偏移避免遮挡
  })
  .attr("text-anchor", "middle")
  .attr("font-size", "12px")
  .attr("fill", "darkred");

内容的提问来源于stack exchange,提问作者The Dead Man

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 16:24:55