Nils
Nils

Reputation: 73

D3 transition on enter and on hover (donut chart)

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

Answers (1)

readyplayer77
readyplayer77

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

Related Questions