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

