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

D3 v3多列柱状图迁移至v7时分组内柱形未渲染问题修复

D3 v3 多列柱状图移植到v7版本时分组内柱形不渲染问题

问题现象

基于D3 v3开发的多列柱状图迁移到D3 v7版本后,外层柱形分组节点可正常生成,但分组内部的柱形rect元素始终未被创建。

  • 原错误实现代码:
// 系列分组渲染
let series = this.seriesContainer.selectAll('.series')
    .data(this.data.series);

series.enter()
    .append('g')
    .classed('series', true)
    .attr('fill', (s, i) => colorTable[i])
    .attr('transform', (s, i) => this.createTranslateProperty((i * (categoryScale.bandwidth() / this.data.series.length))));
series.exit().remove();

// 柱形渲染
let dataSelector = (s: ColumnChartDataSeriesModelI) => s.values

let columns = series.selectAll('.columnChartColumn').data(dataSelector);
columns.enter()
    .append('rect')
    .classed('columnChartColumn', true);

columns.attr("rx", 5)
    .attr("font-size", 10)
    .attr('width', (categoryScale.bandwidth() / this.data.series.length) - 2)
    .attr('height', 10)
    .attr('y', 10)
    .attr('x', (v, i) => categoryScale(this.data.categories[i]));

columns.exit().remove();
  • 对应数据模型定义:
export interface ColumnChartDataSeriesModelI {
    values: Array<number>
};
  • D3 v7运行后生成的异常DOM结构:
...
<g class="columnChartSeriesContainer">
  <g class="series" fill="#0084F5" transform="translate(0,0)"></g>
  <g class="series" fill="#FA8F32" transform="translate(23,0)"></g>
</g>
...
  • D3 v3运行时生成的正常DOM结构:
<g class="columnChartSeriesContainer">
   <g class="series" fill="#0084F5" transform="translate(0,0)">
      <rect class="columnChartColumn" rx="5" font-size="10" width="23" height="270" y="0" x="1"></rect>
      <rect class="columnChartColumn" rx="5" font-size="10" width="23" height="210" y="60" x="63"></rect>
      <rect class="columnChartColumn" rx="5" font-size="10" width="23" height="150" y="120" x="125"></rect>
      <rect class="columnChartColumn" rx="5" font-size="10" width="23" height="150" y="120" x="187"></rect>
      <rect class="columnChartColumn" rx="5" font-size="10" width="23" height="240" y="30.000000000000014" x="249"></rect>
   </g>
   <g class="series" fill="#FA8F32" transform="translate(25,0)">
      <rect class="columnChartColumn" rx="5" font-size="10" width="23" height="60" y="210" x="1"></rect>
      <rect class="columnChartColumn" rx="5" font-size="10" width="23" height="180" y="90.00000000000001" x="63"></rect>
      <rect class="columnChartColumn" rx="5" font-size="10" width="23" height="89.99999999999997" y="180.00000000000003" x="125"></rect>
      <rect class="columnChartColumn" rx="5" font-size="10" width="23" height="30" y="240" x="187"></rect>
      <rect class="columnChartColumn" rx="5" font-size="10" width="23" height="60" y="210" x="249"></rect>
   </g>
</g>

问题根因

D3 v4及后续版本(含v7)重构了选择集逻辑,选择集为不可变对象,方法调用不会修改原选择集,只会返回新的选择集;而v3版本中enter()创建的节点会自动追加到原选择集上。原写法中series变量仅保存了.data()返回的更新选择集,首次渲染无存量节点时该选择集为空,后续enter()创建的分组g节点存在于独立的进入选择集中,没有被赋值给series变量。后续基于空的series选择集查询内部柱形、绑定数据时,自然无法选中新创建的分组节点,也就无法在分组内生成柱形rect。

修复代码

需要将enter()追加节点后的返回选择集赋值给变量,基于该选择集完成后续属性设置、内部元素渲染逻辑:

// 系列分组渲染
let series = this.seriesContainer.selectAll('.series').data(this.data.series)
    .enter()
    .append('g')
    .classed('series', true);

series.attr('fill', (s, i) => colorTable[i])
    .attr('transform', (s, i) => this.createTranslateProperty((i * (categoryScale.bandwidth() / this.data.series.length))));
    
series.exit().remove();

// 柱形渲染
let dataSelector = (s: ColumnChartDataSeriesModelI) => s.values
let columns = series.selectAll('.columnChartColumn').data(dataSelector).enter()
    .append('rect')
    .classed('columnChartColumn', true);

columns.attr("rx", 5)
    .attr('width', (categoryScale.bandwidth() / this.data.series.length) - 2)
    .attr('height', v => columnMaxSize - columnScale(v))
    .attr('y', v => columnScale(v))
    .attr('x', (v, i) => categoryScale(this.data.categories[i]));

columns.exit().remove();

内容的提问来源于stack exchange,提问作者Michał Adamczyk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 10:12:16