Angular应用中D3饼图进入动画问题:添加arcTween报错
解决Angular中D3饼图首次进入页面的动画问题
我明白你作为D3新手遇到的困惑——切换数据时动画正常,但首次渲染却没有过渡效果,尝试在enter阶段加动画还报错。这其实是因为你当前的代码只处理了更新阶段(update)的元素过渡,而首次加载的元素属于进入阶段(enter),需要单独为它们设置从初始状态到目标状态的动画。
问题根源
首次渲染时,所有饼图切片都是通过enter()创建的新元素,你现在直接给它们设置了最终的d属性(路径),没有任何过渡过程。而切换数据时,元素属于update集合,你通过attrTween实现了从旧状态到新状态的插值动画,所以能正常运行。
解决方案:给Enter阶段元素添加初始过渡
我们需要给enter的切片设置一个初始状态(比如角度从0开始),然后通过transition和attrTween过渡到目标角度,这样就能实现首次进入的展开动画。
修改你的代码如下:
1. 调整Inner Slice的Enter逻辑
// Inner Slices Enter/Update/Remove (for DOUGHNUTS) const innerSliceUpdate = slices.selectAll(`.innerSlice${this.id}`).data(data); const innerSliceEnter = innerSliceUpdate.enter().append('path').attr('class', `innerSlice${this.id}`); innerSliceUpdate.exit().remove(); innerSliceEnter .style('fill', (d: any) => hsl(d.data.color).darker(0.7).toString()) .style('transition', SLICE_ANIME) // 设置初始状态:角度从0开始,生成空的路径 .attr('d', () => { const initialArc = { startAngle: 0, endAngle: 0 }; return this.pieInner(initialArc, radius.x + 0.5, radius.y + 0.5, radius.z, height); }) .each(d => { this.interpolators[d.data.label] = d; }) // 添加首次进入的过渡动画:从0角度插值到目标角度 .transition().duration(ANIME_DURATION * 1e3) .attrTween('d', (d) => { // 创建从初始空状态到目标数据的插值器 const interpolate = d3.interpolate({ startAngle: 0, endAngle: 0 }, d); return (t) => this.pieInner(interpolate(t), radius.x + 0.5, radius.y + 0.5, radius.z, height); }); // Update阶段的逻辑保持不变 innerSliceUpdate .transition().duration(ANIME_DURATION * 1e3) .attrTween('d', d => (t, i = this.interpolatorFn(d)(t)) => this.pieInner(i, radius.x + 0.5, radius.y + 0.5, radius.z, height));
2. 调整Top Slice的Enter逻辑
// Top Slices Enter/Update/Remove (facing up x/y axis) const topSliceUpdate = slices.selectAll(`.topSlice${this.id}`).data(data); const topSliceEnter = topSliceUpdate.enter().append('path').attr('class', `topSlice${this.id}`); topSliceUpdate.exit().remove(); topSliceEnter .style('fill', d => d.data.color) .style('stroke', d => d.data.color) .style('transition', SLICE_ANIME) // 设置初始空状态 .attr('d', () => { const initialArc = { startAngle: 0, endAngle: 0 }; return this.pieTop(initialArc, radius.x, radius.y, radius.z); }) .each(d => { this.interpolators[d.data.label] = d; }) // 添加首次过渡动画 .transition().duration(ANIME_DURATION * 1e3) .attrTween('d', (d) => { const interpolate = d3.interpolate({ startAngle: 0, endAngle: 0 }, d); return (t) => this.pieTop(interpolate(t), radius.x, radius.y, radius.z); }); // Update阶段的逻辑保持不变 topSliceUpdate .transition().duration(ANIME_DURATION * 1e3) .attrTween('d', d => (t, i = this.interpolatorFn(d)(t)) => this.pieTop(i, radius.x, radius.y, radius.z));
为什么之前会报错?
你之前尝试在enter阶段直接复用update的interpolatorFn,但这个函数依赖于this.interpolators中保存的旧状态数据——而首次渲染时interpolators里还没有对应的值,所以会出现找不到插值起点的错误。现在我们直接从{ startAngle: 0, endAngle: 0 }开始插值,不需要依赖旧状态,就解决了报错问题。
这样修改后,首次进入页面时,饼图会从0角度平滑展开到目标状态,切换数据时的动画也能正常保留。
内容的提问来源于stack exchange,提问作者Joy Biswas
相关产品推荐
相关产品推荐

