D3 PIE图表在进入并更新角度应用程序时进行动画处理



我正在我的应用程序中使用D3饼图来可视化一些数据。我能够正确显示数据。我只想让图表在进入时具有动画效果。当我更改数据时,它的动画效果似乎很好。我在这里模拟了代码

https://stackblitz.com/edit/angular-ivy-kuysd8

主要问题是当我在'enter'中添加arcTween时,它会抛出一个错误

const data = pie().sort(null).value((d: any) => d.value)(rawData);
const { height, radius } = this.dimensions;
const slices = this.svg.select(`.slices${this.id}`);
// 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)
.attr('d', d => this.pieInner(d, radius.x + 0.5, radius.y + 0.5, radius.z, height))
.each(d => { this.interpolators[d.data.label] = d; });
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));
// 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', d => this.pieTop(d, radius.x, radius.y, radius.z))
.each(d => { this.interpolators[d.data.label] = d; });
// ****** TRYING TO ADD AN ARC TWEEN HERE *******
topSliceUpdate
.transition().duration(ANIME_DURATION * 1e3)
.attrTween('d', d => (t, i = this.interpolatorFn(d)(t)) => this.pieTop(i, radius.x, radius.y, radius.z));

D3新手,如有任何帮助,将不胜感激

注意:寻找一个解决方案来学习和理解我做错了什么

这段代码

topSliceEnter
....
.attr("d", d => this.pieTop(d, radius.x, radius.y, radius.z))

明确地说,新添加的项目将以完成状态进行渲染,这没有多大意义,如果你想为某个东西设置动画,那么你需要从它开始设置动画。

假设您想从{startAngle: 0, endAngle: 0.1}中为图表设置动画。希望你没有期望我重构你所有的代码,所以我修复了topSlice部分:https://stackblitz.com/edit/angular-ivy-f7pvyz?file=src%2Fapp%2Fapp.component.ts

topSliceEnter 
......
.attr("d", d => this.pieTop({startAngle: 0, endAngle: 0.1}, radius.x, radius.y, radius.z))
.each(d => {
this.interpolators[d.data.label] = {...d, startAngle: 0, endAngle: 0.1};
// I would refactor this. the interpolation logic looks strange, but still this will make your example animate fine.
});
topSliceUpdate.merge(topSliceEnter) // merge will make next transition to be applied on both entering and updating elements
.transition()
...

第页。S.你的代码中有一些错误,不能正确处理更新,例如颜色变化

实现动画的一种方法是使用与当前初始化不同的值初始化图表切片,然后使用ngAfterViewInit((等生命周期挂钩来设置实际的初始值。基于您现有的代码,类似于:

export class AppComponent  {
name = 'Angular ' + VERSION.major;
data = [
{ key: 'inprogress', color: 'yellow', label: 'In Progress', value: 0 },
{ key: 'completed', color: 'green', label: 'Completed', value: 0 },
{ key: 'delayed', color: 'red', label: 'Delayed', value: 0 },
];  
ngAfterViewInit(){
this.data = [
{ key: 'inprogress', color: 'yellow', label: 'In Progress', value: 3 },
{ key: 'completed', color: 'green', label: 'Completed', value: 18 },
{ key: 'delayed', color: 'red', label: 'Delayed', value: 5 },
];
}
constructor(){
setTimeout(() => {
this.data = [
{ key: 'inprogress', color: 'yellow', label: 'In Progress', value: 5 },
{ key: 'completed', color: 'green', label: 'Completed', value: 5 },
{ key: 'delayed', color: 'red', label: 'Delayed', value: 15 },
]
}, 2000)
}
}

下面是另一个hacky解决方案,尽管更容易实现。它使用了与@mamishra 相同的想法

piechard.component.ts中更改ngAfterViewInit()

ngAfterViewInit(): void {
this.id = `__${Math.floor(Math.random() * 5e10)}`;
this.init();
if (this.data && this.data.length >= 0) {
this.draw(this.data.map(item => ({...item, value: 1}))); // <-- Simply add this line
this.draw(this.data);
}
}

这个想法是为了让用户看到相等的分区,这些分区被动画化为最终值

参见示例实施

最新更新