React环境下如何检测D3图表完全加载(含渲染动画执行完毕)
window.load和DOMContentLoaded只会监听原生DOM的加载解析流程,和D3内部的JS动画逻辑完全无关,所以必然会提前触发,要解决这个问题必须基于D3本身的动画生命周期API实现监听:
- 方案1:监听D3过渡(transition)的end事件(兼容所有D3版本)
D3的所有过渡动画都支持通过on("end", callback)绑定完成回调,如果存在多个并行/串行动画,可以把每个动画包装为Promise,用Promise.all批量监听全部完成事件。
示例代码:
import { useEffect, useState } from 'react'; import * as d3 from 'd3'; function ChartComponent() { const [d3AnimationDone, setD3AnimationDone] = useState(false); useEffect(() => { const svg = d3.select('#chart-container') .append('svg') .attr('width', 500) .attr('height', 300); // 示例:包装两个D3动画为Promise const anim1 = new Promise(resolve => { svg.append('circle') .attr('cx', 50) .attr('cy', 50) .attr('r', 10) .transition() .duration(1000) .attr('cx', 200) .on('end', resolve); }); const anim2 = new Promise(resolve => { svg.append('rect') .attr('x', 50) .attr('y', 100) .attr('width', 20) .attr('height', 20) .transition() .duration(1500) .attr('x', 200) .on('end', resolve); }); // 所有动画完成后触发React侧逻辑 Promise.all([anim1, anim2]).then(() => { setD3AnimationDone(true); }); // 清理函数:组件卸载时中断动画避免内存泄漏 return () => { svg.selectAll('*').interrupt(); }; }, []); return ( <div> <div id="chart-container"></div> {/* 动画完成后才渲染的依赖内容 */} {d3AnimationDone && <div>依赖D3图表的后续内容</div>} </div> ); }
- 方案2:使用D3 v7+内置的transition.end()方法
D3 v7及以上版本的transition实例自带end()方法,直接返回Promise,不需要手动绑定end事件,代码更简洁:
// 单个动画 const anim = svg.append('circle') .transition() .duration(1000) .attr('cx', 200) .end(); // 多个动画同理用Promise.all监听 Promise.all([anim1, anim2]).then(() => { setD3AnimationDone(true); });
- 兜底方案:MutationObserver监听DOM变化
如果项目中D3动画逻辑分散不好逐一统计,可以监听D3图表容器的DOM属性变化,当一段时间内没有发生任何DOM修改时,判定为动画结束。该方案准确性稍差,仅作为兜底使用。
内容的提问来源于stack exchange,提问作者altern
相关产品推荐
相关产品推荐

