如何在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
相关产品推荐
相关产品推荐

