Reputation: 679
I am trying to transform dots along a circular path. Each transform has a different duration. On end the transition start again for all dots once the transform with the shortest speed duration ends. How do I re-start the transition for each dot independently?
const graphWidth = 500;
const graphHeight = 500;
const svg = d3.select("svg")
.attr("width", graphWidth)
.attr("height", graphHeight);
const testData = [{
"country": "Netherlands",
"speed": 4000
},
{
"country": "Belgium",
"speed": 2000
},
{
"country": "Austria",
"speed": 1000
},
{
"country": "Germany",
"speed": 5000
},
{
"country": "USA",
"speed": 4000
}
];
const dots = svg.selectAll('circle')
.data(testData)
.enter()
.append('circle')
.attr('transform', `translate(${graphWidth / 2}, ${graphHeight / 2})`)
.attr('cx', 223)
.attr('r', 5)
.attr('fill', 'yellow')
.attr('stroke', 'black');
function transition() {
svg.selectAll('circle').each(function(d) {
d3.select(this)
.transition()
.ease(d3.easeLinear)
.duration(d.speed)
.attrTween('transform', rotTween)
.on('end', transition);
})
}
transition();
function rotTween() {
var i = d3.interpolate(0, 360);
return function(t) {
return `translate(${graphWidth / 2}, ${graphHeight / 2}) rotate(${i(t)}, 0, 0)`;
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>
Upvotes: 1
Views: 154
Reputation: 13129
The easiest way is to change your transition
function to handle just one transition of one circle, by taking the .each()
out of the function:
const graphWidth = 500;
const graphHeight = 500;
const svg = d3.select("svg")
.attr("width", graphWidth)
.attr("height", graphHeight);
const testData = [{
"country": "Netherlands",
"speed": 4000
},
{
"country": "Belgium",
"speed": 2000
},
{
"country": "Austria",
"speed": 1000
},
{
"country": "Germany",
"speed": 5000
},
{
"country": "USA",
"speed": 4000
}
];
const dots = svg.selectAll('circle')
.data(testData)
.enter()
.append('circle')
.attr('transform', `translate(${graphWidth / 2}, ${graphHeight / 2})`)
.attr('cx', 223)
.attr('r', 5)
.attr('fill', 'yellow')
.attr('stroke', 'black');
function transition(circle, d) {
d3.select(circle)
.transition()
.ease(d3.easeLinear)
.duration(d.speed)
.attrTween('transform', rotTween)
.on('end', function() {
transition(circle, d);
});
}
svg.selectAll('circle').each(function(d) {
transition(this, d);
});
function rotTween() {
var i = d3.interpolate(0, 360);
return function(t) {
return `translate(${graphWidth / 2}, ${graphHeight / 2}) rotate(${i(t)}, 0, 0)`;
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>
Alternatively, you can name the otherwise anonymous function you pass to .each()
(in this case I named it repeat
), and then call that function again in the .on("end"
clause
const graphWidth = 500;
const graphHeight = 500;
const svg = d3.select("svg")
.attr("width", graphWidth)
.attr("height", graphHeight);
const testData = [{
"country": "Netherlands",
"speed": 4000
},
{
"country": "Belgium",
"speed": 2000
},
{
"country": "Austria",
"speed": 1000
},
{
"country": "Germany",
"speed": 5000
},
{
"country": "USA",
"speed": 4000
}
];
const dots = svg.selectAll('circle')
.data(testData)
.enter()
.append('circle')
.attr('transform', `translate(${graphWidth / 2}, ${graphHeight / 2})`)
.attr('cx', 223)
.attr('r', 5)
.attr('fill', 'yellow')
.attr('stroke', 'black');
svg.selectAll('circle')
.each(function repeat(d) {
d3.select(this)
.transition()
.ease(d3.easeLinear)
.duration(function(d) { return d.speed; })
.attrTween('transform', rotTween)
.on("end", repeat);
});
function rotTween() {
var i = d3.interpolate(0, 360);
return function(t) {
return `translate(${graphWidth / 2}, ${graphHeight / 2}) rotate(${i(t)}, 0, 0)`;
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>
Upvotes: 1