Reputation: 6207
I'm missing something in my understanding here rather than a bug I think...
In d3, I'm trying to update multiple attribute values within a transition. Using multiple transition.attr
declarations works fine. But I'd like to use .each
as in the real world I have a big calculation I don't want to do twice or more... I tried using transition.each
but the attribute values don't interpolate.
Is it the d3.select
within the each
? I tried d3.active
but that didn't do anything.
(There is a https://github.com/d3/d3-selection-multi library that supplied .attrs
that worked nicely up to v5, but not with the new v6...)
See https://jsfiddle.net/f679a4yj/3/ - what I can do to get the animation but through the .each
, what am I not getting?
Upvotes: 1
Views: 174
Reputation: 102188
The problem here is a misconception regarding transition.each
: you cannot use it to wrap several transition.attr
or transition.style
methods. In fact, it is used only to invoke arbitrary code to each element in that transition, just like selection.each
.
Therefore, when you do...
.each (function (d, i) {
// complicated stuff with d I don't want to do twice
d3.select(this)
.attr("x", ((zz+i)%4) *20)
.attr("y", ((zz+i)%4) *40)
})
... you are simply changing the attribute in the DOM element, and the element will immediately jump to the new x
and y
position, as expected. In other words, that d3.select(this).attr(...
in your code is a selection.attr
, not a transition.attr
.
You have several alternatives (for avoiding redundant complex calculations). If you want to stick with transition.each
, you can use it to create a new local variable, or maybe a new property. For that to work, we need to have objects as the data elements in the array, and then we just use transition.attr
as usual:
.each(function(d, i) {
d.complexValue = ((zz + i) % 4) * 20;
})
.attr("x", function(d) {
return d.complexValue;
})
//etc...
Here is a demo:
const sel = d3.select("svg")
.selectAll(null)
.data([{
color: "red"
}, {
color: "blue"
}, {
color: "green"
}, {
color: "yellow"
}])
.enter()
.append("rect")
.attr("width", 30)
.attr("height", 30)
.attr("x", function(d, i) {
return i * 20;
})
.attr("y", function(d, i) {
return i * 40;
})
.style("fill", function(d) {
return d.color;
});
let zz = 0;
function each() {
zz++;
d3.selectAll("rect")
.transition()
.duration(1000)
.each(function(d, i) {
d.complexValue = ((zz + i) % 4) * 20;
})
.attr("x", function(d) {
return d.complexValue;
})
.attr("y", function(d) {
return d.complexValue;
})
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="demo"><svg width="360px"></svg></div>
<button onclick="each()">In an Each</button>
Upvotes: 2