Reputation: 73
Even though there are a lot of questions already around this topic I still cannot get it right.
I am drawing a donut chart like this:
const svg = d3.select(svgRef.current);
const size = 500;
const radius = size / 2;
;
svg.attr("viewBox", [-size / 2, -size / 2, size, size]);
const pie = d3.pie()
.padAngle(0.02)
.sort(null)
.value(d => d.value);
const arc = d3.arc()
.innerRadius(radius * 0.67)
.outerRadius(radius - 1);
const color = d3.scaleOrdinal()
.domain(data.map(d => d.name))
.range(d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), data.length).reverse());
const arcs = pie(data);
svg.selectAll("path")
.data(arcs)
.join("path")
.attr("fill", d => color(d.data.name))
.transition()
.delay((_,i) => i * 500)
.duration(500)
.attrTween('d', (d) => {
const i = d3.interpolate(d.startAngle+0.1, d.endAngle);
return (t) => {
d.endAngle = i(t);
return arc(d)
}
});
The chart animates as expected. The question is: I want the segments to expand a bit on hover. How do I do that?
Where exactly does the .on('mouseover', ...)
go? I know I need another attrTween but I don't know how.
Thanks a lot!
EDIT:
I found a way now but I am still wondering whether there is a better way to do this?
const svg = d3.select(svgRef.current);
const size = 500;
const radius = size / 2;
;
svg.attr("viewBox", [-size / 2, -size / 2, size, size]);
const pie = d3.pie()
.padAngle(0.02)
.sort(null)
.value(d => d.value);
const initialArc = d3.arc()
.innerRadius(radius * 0.67)
.outerRadius(radius - 20);
const transformedArc = d3.arc().innerRadius(radius * 0.67);
const color = d3.scaleOrdinal()
.domain(data.map(d => d.name))
.range(d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), data.length).reverse());
const arcs = pie(data);
function arcTween(nextOuterRadius, currentOuterRadius) {
return function() {
d3.select(this).transition().duration(500).attrTween("d", function(d) {
const i = d3.interpolate(currentOuterRadius, nextOuterRadius);
return (t) => { d.outerRadius = i(t); return transformedArc(d); };
});
};
}
svg.selectAll("path")
.data(arcs)
.join("path")
.attr("fill", d => color(d.data.name))
.on("mouseover", arcTween(radius, radius - 20))
.on('mouseout', arcTween(radius - 20, radius))
.transition()
.delay((_,i) => i * 500)
.duration(500)
.attrTween('d', (d) => {
const i = d3.interpolate(d.startAngle+0.1, d.endAngle);
return (t) => {
d.endAngle = i(t);
return initialArc(d)
}
});
Upvotes: 0
Views: 712
Reputation: 2159
If you're trying to add the effect to all the path
elements, you can place it before the transition
or save the selections to a variable and append the transition and mouseover events separately.
svg.selectAll("path")
... // Your existing code
.on('mouseover', function () {
svg.selectAll('[YOUR_ELEMENTS]')
.transition()
});
.transition()
... // initial transition
or
let pies = svg.selectAll("path")
... // Your existing code
.on('mouseover', function () {
svg.selectAll('[YOUR_ELEMENTS]')
.transition()
});
pies.transition()
... // initial transition
pies.on('mouseover', function () {
svg.selectAll('[YOUR_ELEMENTS]')
.transition()
});
Upvotes: 1