D3.js使用二级嵌套数据绘制图形符号并将所有圆点置于同一g组
问题核心原因
d3.selection.data()的第二个参数是数据匹配键函数,仅用于数据更新时关联已有DOM和对应数据,不会改变绑定到元素的原始数据结构。你当前直接传入games数组作为第一个参数,所以每个circle绑定的自然是games数组的顶级元素(即带name和values的整条折线数据对象),和你预期的子级数据完全不符。- 要把所有折线的圆点放在同一个
<g>下,需要先把嵌套的二维数据扁平化为一维数组,数组的每个元素对应单个圆点的完整信息(包含自身数值、所属折线的名称等),总长度等于折线数量 × 40。
解决方案代码
var symbolsb = d3.select("#plot-b") // plot-b是svg内的绘图区域组 .append("g") .attr("id", "symbols-b"); // 第一步:扁平化嵌套数据,同时保留父级折线的信息 const flatCircleData = games.flatMap(function(game) { // 给每个数值项追加所属折线的name,方便后续逻辑调用 return game.values.map(function(valueItem) { return { ...valueItem, // 保留原values里的date、count等字段 seriesName: game.name } }) }) // 第二步:将扁平化后的一维数组直接绑定到circle元素 symbolsb.selectAll("circle") .data(flatCircleData) .enter() .append("circle") .attr("class", "symbolsb") .attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y_count(d.count); }) .attr("r", function(d, i) { return included_names.includes(d.seriesName) && (i % 3 === 1) ? 8 : 0; }) .style("fill", function(d) { return color(d.seriesName); });
补充说明
- 如果需要兼容不支持
flatMap的旧运行环境,可以把扁平化逻辑替换为games.map(...).flat()或者用循环手动拼接数组。 - 扁平化后每个圆点的独立数据都直接绑定到对应DOM元素上,不需要再通过父节点反向获取数据,逻辑更简洁,渲染性能也更高。
内容的提问来源于stack exchange,提问作者supvato
相关产品推荐
相关产品推荐

