带交替标签的简易D3.js折线图优化:铺满SVG、锚定标签及添加标记
D3.js折线图优化实现
1. 折线铺满SVG画布
原代码使用固定偏移计算坐标,无法自适应SVG尺寸。改用D3的scaleLinear比例尺结合边距设置,让折线自动填充SVG的有效绘图区域:
- 定义边距避免内容贴边
- 计算绘图区域的实际宽高(SVG尺寸减去边距)
- 为x轴创建基于数据索引的线性比例尺,y轴创建基于数据范围的线性比例尺
- 更新折线生成函数,通过比例尺计算坐标
2. 百分比标签锚定折线路径
不再硬编码标签位置,通过折线路径的getPointAtLength方法获取线段中点坐标,让标签精准锚定在对应线段上:
- 绘制折线后获取路径总长度,计算单段线段长度
- 遍历数据获取每段线段的中点坐标
- 根据涨跌方向调整标签的垂直偏移,保证标签不遮挡折线
3. 可选标记点功能
添加可选参数控制是否显示数据点标记,默认关闭,开启时在每个数据点位置绘制圆形标记:
- 函数参数中加入
showMarkers = false作为开关 - 若参数为
true,遍历数据创建圆形元素,通过比例尺定位
优化后完整代码
<html> <head> <script src="https://d3js.org/d3.v5.min.js"></script> <script> function createLineChart(data, showMarkers = false) { // SVG尺寸与边距设置 const svgWidth = 800; const svgHeight = 600; const margin = { top: 20, right: 20, bottom: 20, left: 20 }; const plotWidth = svgWidth - margin.left - margin.right; const plotHeight = svgHeight - margin.top - margin.bottom; // 创建SVG容器并偏移边距 const svg = d3.select("body").append("svg") .attr("width", svgWidth) .attr("height", svgHeight) .append("g") .attr("transform", `translate(${margin.left}, ${margin.top})`); // 创建比例尺 const xScale = d3.scaleLinear() .domain([0, data.length - 1]) .range([0, plotWidth]); const yScale = d3.scaleLinear() .domain([0, d3.max(data)]) .range([plotHeight, 0]); // 创建生成折线的函数 const line = d3.line() .x((d, i) => xScale(i)) .y(d => yScale(d)); // 绘制折线 const linePath = svg.append("path") .datum(data) .attr("d", line) .attr("stroke", "black") .attr("stroke-width", 2) .attr("fill", "none"); // 绘制锚定到折线的百分比标签 const pathLength = linePath.node().getTotalLength(); const segmentLength = pathLength / (data.length - 1); data.forEach((d, i) => { if (i > 0) { const percentChange = ((d - data[i - 1]) / data[i - 1]) * 100; const color = percentChange >= 0 ? "green" : "red"; // 获取线段中点坐标 const midPoint = linePath.node().getPointAtLength((i - 0.5) * segmentLength); // 根据涨跌调整标签偏移 const yOffset = percentChange >= 0 ? -15 : 15; svg.append("text") .text(`${percentChange.toFixed(1)}%`) .attr("x", midPoint.x) .attr("y", midPoint.y + yOffset) .attr("text-anchor", "middle") .attr("fill", color) .attr("font-size", "12px"); } }); // 可选:绘制数据标记点 if (showMarkers) { svg.selectAll(".marker") .data(data) .enter() .append("circle") .attr("class", "marker") .attr("cx", (d, i) => xScale(i)) .attr("cy", d => yScale(d)) .attr("r", 4) .attr("fill", "steelblue") .attr("stroke", "white") .attr("stroke-width", 1.5); } } </script> </head> <body> <script> const data = [10, 20, 15, 40, 50, 60]; // 调用时传入true开启标记点,不传则默认关闭 createLineChart(data, true); </script> </body> </html>
内容的提问来源于stack exchange,提问作者Zach Kramer
相关产品推荐
相关产品推荐

