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

Angular7搭配D3v7在表格单元格渲染面积图出现循环重复渲染问题

问题根因

  1. 核心原因:你将D3渲染方法直接写在了模板插值{{}}中,Angular变更检测每次触发都会执行该方法,而方法内每次都会调用.append('svg')新增SVG元素,最终导致重复渲染。
  2. 代码笔误:TS方法的第二个入参是code,但DOM选择时错误使用了未定义的fleet变量;X轴比例尺定义域引用了未定义的data变量,应为处理后的temp1;第二个图表容器调用方法时错误传了A参数,导致两个图表数据一致。

修复方案

1. 调整模板代码

移除插值中的方法调用,仅标记图表容器,同时为*ngFor添加trackBy减少不必要的重渲染:

<tr [id]="'row' + '-' + fleet" *ngFor="let fleet of fleetcolumn; trackBy: trackByFleet">
  <td>{{ fleet }}</td>
  <ng-container *ngFor="let station of formattedBaseStations | slice: 1; trackBy: trackByStation">
    <td style="width: 28em;">
      <mat-grid-list cols="2" rowHeight="90px">
        <mat-grid-tile class="captainChart">
          <div [id]="'demandChartInTable1-' + station + '-' + fleet + '-A'"></div>
        </mat-grid-tile>
        <mat-grid-tile class="FOChart">
          <div [id]="'demandChartInTable1-' + station + '-' + fleet + '-B'"></div>
        </mat-grid-tile>
      </mat-grid-list>
    </td>
  </ng-container>
</tr>

2. 调整TS逻辑

将渲染逻辑移到ngAfterViewInit生命周期中执行,每次渲染前先清空容器避免重复生成元素:

// 新增trackBy方法优化ngFor性能
trackByFleet(index: number, item: string) {
  return item;
}

trackByStation(index: number, item: string) {
  return item;
}

ngAfterViewInit() {
  // 视图加载完成后批量渲染所有图表
  this.fleetcolumn.forEach(fleet => {
    (this.formattedBaseStations.slice(1) as string[]).forEach(station => {
      this.renderChart(station, fleet, 'A');
      this.renderChart(station, fleet, 'B');
    });
  });
}

// 改造后的渲染方法
renderChart(station: string, code: string, position: string) {
  const containerSelector = `#demandChartInTable1-${station}-${code}-${position}`;
  const container = d3.select(containerSelector);
  // 清空容器已有内容,防止重复渲染
  container.html('');

  // 处理数据
  const temp1 = this.data
    .filter(d => d.baseCode == station && d.code == code && d.position == position)
    .map(d => ({
      date: d3.timeParse('%Y-%m-%d')(d.date),
      hours: d.hours
    }));

  const chartContainer = document.querySelector('.captainChart');
  if (!chartContainer) return;

  const margin = { top: 0, right: 0, bottom: 1, left: 1 };
  const width = chartContainer.clientWidth - margin.left - margin.right;
  const height = 90 - margin.top - margin.bottom;

  // 生成SVG
  const svg = container
    .append('svg')
    .attr('width', chartContainer.clientWidth + margin.left + margin.right)
    .attr('height', chartContainer.clientHeight + margin.bottom)
    .append('g')
    .attr('transform', `translate(${margin.left},${margin.top})`);

  // 定义比例尺
  const x = d3.scaleTime()
    .domain(d3.extent(temp1, d => d.date))
    .range([0, chartContainer.clientWidth]);

  const y = d3.scaleLinear()
    .domain([0, 500])
    .range([chartContainer.clientHeight + margin.bottom, 0]);

  // 绘制面积图
  svg.append('path')
    .datum(temp1)
    .attr('fill', '#cce5df')
    .attr('stroke', '#69b3a2')
    .attr('stroke-width', 1.5)
    .attr('d', d3.area()
      .x(d => x(d.date))
      .y0(y(0))
      .y1(d => y(d.hours))
    );
}

如果后续数据有更新,在数据更新的回调中重新调用renderChart方法即可,不要在模板中直接调用渲染逻辑。

内容的提问来源于stack exchange,提问作者user10368959

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 03:06:04