如何用D3.js生成单SVG多列布局的Actigram(活动图)
问题描述
我需要绘制多列布局的Actigram(活动图),目前用D3.js实现了多SVG折线图(测试代码如下),但希望把这些独立SVG合并成单个SVG;同时想了解绘制时间序列数据的巧妙方法,也愿意尝试更简便的其他可视化库。
现有测试代码
JavaScript
const signalData = [{ name: "Signal 1", data: [1, 2, 3, 4, 5, 6, 7], }, { name: "Signal 2", data: [2, 3, 1, 4, 5, 1, 3], }, { name: "Signal 3", data: [1, 7, 2, 6, 3, 5, 4], }, ]; // This is a line generator. Normally, you pass the x- and y-scales in, // see also d3-scale on github const line = d3.line() // The first argument is always the datum object, the second is the index // Note that I use the index to determine the x coordinate of each point here .x((d, i) => i * 50) // And the datum object for the y-coordinate .y(d => 150 - (15 * d)); // The term `datum` is unrelated to date, but is the singular value of `data`. // One datum, many data. d3.select("#plotsHere") .selectAll("svg") // Append one svg per array entry, look up the so-called enter, update, exit // cycle for this. It's the most complex part of d3 .data(signalData) .enter() .append("svg") // Execute a custom function for each element. People who are new to d3.js // over-use this function, very often you don't need it! .each(function(d, i) { // I pass the current svg element, the datum object, and the index for convenience draw(d3.select(this), d, i); }); function draw(svg, data, index) { // Append a label, set the text to the name svg.append("text") .attr("x", 20) .attr("y", 20) .text(d => d.name); // Append a path, take the datum of the svg, pick it's `data` property, // which is the array of numbers, and set that as the datum of the path, // then call the line generator svg.append("path") .datum(d => d.data) .attr("d", line); }
CSS
path { stroke: darkblue; stroke-width: 2px; fill: none; } svg { border: solid 2px red; } #plotsHere { display: grid; grid-auto-flow: row; grid-gap: 0px; grid-template-rows: 150px 150px; grid-template-columns: 300px 300px; }
HTML
<script src="https://d3js.org/d3.v7.min.js"></script> <body> <div id="plotsHere"> </div> </body>
解决方案
一、用D3.js合并为单个SVG
核心思路是在单个SVG内用<g>元素分组每个信号的折线和标签,通过计算分组位置实现多列布局,替代多SVG方案。
修改后的代码
JavaScript
const signalData = [{ name: "Signal 1", data: [1, 2, 3, 4, 5, 6, 7], }, { name: "Signal 2", data: [2, 3, 1, 4, 5, 1, 3], }, { name: "Signal 3", data: [1, 7, 2, 6, 3, 5, 4], }, ]; // 布局参数统一配置 const svgWidth = 600; const svgHeight = 300; const panelWidth = 300; const panelHeight = 150; const padding = 20; // 用比例尺替代硬编码坐标,适配数据变化 const xScale = d3.scaleLinear() .domain([0, signalData[0].data.length - 1]) .range([padding, panelWidth - padding]); const yScale = d3.scaleLinear() .domain([0, d3.max(signalData.flatMap(d => d.data))]) .range([panelHeight - padding, padding]); const line = d3.line() .x((d, i) => xScale(i)) .y(d => yScale(d)); // 创建单个SVG容器 const svg = d3.select("#plotsHere") .append("svg") .attr("width", svgWidth) .attr("height", svgHeight); // 为每个信号创建分组,计算行列位置 const groups = svg.selectAll("g") .data(signalData) .enter() .append("g") .attr("transform", (d, i) => { const col = i % 2; const row = Math.floor(i / 2); return `translate(${col * panelWidth}, ${row * panelHeight})`; }); // 绘制信号名称标签 groups.append("text") .attr("x", padding) .attr("y", padding) .text(d => d.name); // 绘制折线 groups.append("path") .datum(d => d.data) .attr("d", line) .style("stroke", "darkblue") .style("stroke-width", 2) .style("fill", "none"); // 可选:添加分组边框 groups.append("rect") .attr("x", 0) .attr("y", 0) .attr("width", panelWidth) .attr("height", panelHeight) .style("stroke", "red") .style("fill", "none");
CSS
#plotsHere { margin: 20px; }
HTML
<script src="https://d3js.org/d3.v7.min.js"></script> <body> <div id="plotsHere"> </div> </body>
关键改进点
- 用单个SVG替代多SVG,通过
<g>元素分组管理每个子图,减少DOM节点数量 - 引入D3比例尺,避免硬编码坐标,让图表能自适应数据范围变化
- 统一配置布局参数,后续调整列数、行高、间距只需修改参数即可
二、时间序列绘制技巧
- 时间比例尺适配:如果数据是时间戳,用
d3.scaleTime()替代线性比例尺,自动处理时间轴的刻度、格式和间隔 - 交互增强:添加鼠标悬停显示详情的功能,可通过自定义事件或D3的事件监听实现
- 高效更新:利用D3的enter/update/exit模式,数据变化时仅更新需要修改的元素,避免重绘整个SVG
- 坐标轴优化:通过
d3.axisBottom()和d3.axisLeft()生成x/y坐标轴,提升图表可读性
三、简便可视化库推荐
如果觉得D3.js成本较高,可尝试以下开箱即用的库:
- Chart.js:轻量级,API简洁,支持多系列折线图,内置布局能力,适合快速开发
- Plotly.js:功能丰富,支持交互式图表,自带多子图布局配置,适合复杂可视化需求
- ECharts:配置式开发,内置大量图表类型,多列布局只需配置
grid参数,文档完善
内容的提问来源于stack exchange,提问作者user1505631
相关产品推荐
相关产品推荐

