4👍
Two changes made your snippet work.
The first one was moving the arcTween
method up in the methods
object. Since I’m not a Vue user and as this makes no sense from a JS perspective (the object’s properties order shouldn’t matter), I’ll leave that part for you to investigate.
The second change is just related to the meaning of this
. You’re mixing this
as the method
object (as in this.arc
) and this
as the DOM element (as in this._current
). Like I said in my comment, just use the third and second arguments combined to refer to the DOM element (in the vast majority of D3 methods).
That said, this…
arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return t => {
return this.arc(i(t));
};
}
…should be:
arcTween(a, j, n) {
var i = d3.interpolate(n[j]._current, a);
n[j]._current = i(0);
return t => {
return this.arc(i(t));
};
}
Here is your snippet with those two changes:
new Vue({
el: "#app",
data() {
return {
index: 0,
data: [
[{
key: "one",
value: 123
},
{
key: "two",
value: 232
},
{
key: "three",
value: 186
}
],
[{
key: "one",
value: 145
},
{
key: "two",
value: 270
},
{
key: "three",
value: 159
}
],
]
}
},
mounted() {
// set the dimensions and margins of the graph
var margin = 1;
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
this.radius = Math.min(this.width, this.height) / 2 - margin;
this.width = 250;
this.height = 250;
// append the svg object to the div called 'my_dataviz'
this.svg = d3
.select("#my_dataviz")
.append("svg")
.attr("width", this.width)
.attr("height", this.height)
.append("g")
.attr(
"transform",
"translate(" + this.width / 2 + "," + this.height / 2 + ")"
);
// Compute the position of each group on the pie:
this.pie = d3.pie().value(function(d) {
return d[1];
});
// declare an arc generator function
this.arc = d3
.arc()
.outerRadius(100)
.innerRadius(50);
this.setSlicesOnDoughnut();
},
methods: {
swapData() {
if (this.index === 0) this.index = 1;
else this.index = 0;
this.setSlicesOnDoughnut();
},
arcTween(a, j, n) {
var i = d3.interpolate(n[j]._current, a);
n[j]._current = i(0);
return t => {
return this.arc(i(t));
};
},
setSlicesOnDoughnut() {
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
var data_ready = this.pie(
this.data[this.index].map(function(d) {
return [d["key"], d["value"]];
})
);
//console.log(data_ready);
// join
var arcs = this.svg.selectAll(".arc").data(data_ready);
// update
arcs
.transition()
.duration(1500)
.attrTween("d", this.arcTween);
// enter
arcs
.enter()
.append("path")
.attr("class", "arc")
.attr("fill", "#206BF3")
.attr("stroke", "#2D3546")
.style("stroke-width", "2px")
.attr("d", this.arc)
.each((d, i, n) => {
n[i]._current = d;
});
}
}
})
<div id="app">
<button @click="swapData">Swap</button>
<div id="my_dataviz" class="flex justify-center"></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://d3js.org/d3.v6.js"></script>
0👍
the code really works, you can see the fiddle https://jsfiddle.net/1Lk8zqud/1/
If you really need to have the containers, then you need to have some considerations:
- You need to consider the containers with the full d3 update pattern:
- New containers -> set the attr d, append a path, append an image
3 – Existing containers -> no appends, just update the path and image
4 – remove the containers not needed
In case of new containers, remember that they are added with the final path (no transitions), so its better to lower the old containers in order to dont mess up with the transitions. I didn’t try it with images.
So here is the new version
const g = svg.selectAll('g.arcs')
.data(data_ready, function(d) { return d.index; });
// new data -> add arcs
g.enter().append('g')
.attr('class', 'arcs')
.lower()
.append('path')
.attr("fill", "#206BF3")
.attr("class", "slice")
.attr("stroke", "#2D3546")
.style("stroke-width", "2px")
.transition()
.duration(1500)
.attrTween("d", function(a) {
let i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return this.arc(i(t));
};
});
// old data -> transition data on the container g (for using with the images). Don't add arcs
g.raise()
.transition()
.duration(1500)
.attrTween("d", function(a) {
let i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return this.arc(i(t));
};
});
// transition the path inside the old containers g
g.select('path').transition()
.duration(1500)
.attrTween("d", function(a) {
let i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return this.arc(i(t));
};
})
g.exit().lower().transition().duration(1500).remove();```
A working version of this code:
https://jsfiddle.net/rtLbyn52/