D3.js - Tween arc position, inner radius and outer radius - D3.js Arc

I'm trying to figure out how to twist multiple arcs to new positions, and also resize the arc along the inner and outer radius.

A value means it will move from its current position on the canvas to a new position, and also change its new shape based on changes in the new data compared to the old data.

I have read a lot of examples like http://bl.ocks.org/mbostock/5100636 which are all great, but I cannot adapt any example to my specific need. Most of the examples I've found don't involve moving an arc on the canvas.

this is how I create arcs working fine in the first place and also animating the arc from 0 to its end position according to the data.

// THIS IS CALLED ON LOAD OF THE GRAPH
function createLargeArcs(graph,data){
    var arc = d3.svg.arc()
                .innerRadius(function (data) { return (data.r * 5) + 5; })
                .outerRadius(function (data) { return data.r * 5 })
                .startAngle(0);


    var endAngleFunction = function (data) {
        return (data.percentage) * ((2 * Math.PI) / 180);
    };

    // a global variable
    arcGroup = graph.append("g")
        .attr("class", "arcsGroup");

    var arcs = arcGroup.selectAll("path")
                        .data(data)
                        .enter()
                        .append("svg:path")
                        .attr("transform", function (d) { return "translate(" + xScale(d.u) + "," + yScale(d.t) + ")"; })
                        .style("fill", "red");

    arcs.transition()
        .duration(750)
        .call(arcTween);

    // http://bl.ocks.org/mbostock/5100636
    function arcTween(transition, newAngle) {
        transition.attrTween("d", function (d) {
            var interpolate = d3.interpolate(0, endAngleFunction(d));
            return function (t) {
                d.endAngle = interpolate(t);
                return arc(d);
            };
        });
    }
  }

      

  • all of the above works just fine

But his process of devising how to twist arcs to their new positions and their new size is based on the new data values ​​I'm struggling with.

so far I have an update function that accepts a new / updated dataset and inside an update function where I target the animation of the arc elements. I achieved moving all arcs to their new positions with the following:

// THIS IS CALLED LATER ON WHEN DATA CHANGES - ITS HERE I NEED TO IMPLEMENT
function refresh(data){

    var arcs = arcGroup.selectAll("path")
                        .data(newData)

    arcs.transition()
        .duration(1000)
        .attr("transform", function(d) { return "translate("+ xScale(d.users) + "," + yScale(d.traffic) + ")"; });
}

      

Any help would be much appreciated. Thanks to

+3


source to share


1 answer


Ok, so I finally worked out what seems to be the best approach for this use case. So, as you can see from the example above, I was able to create arcs without problems, but the task of transitioning position, radius and arc radians at the same time across multiple arcs (10 in this case), it started a little more difficult.

Most examples such as http://bl.ocks.org/mbostock/5100636 demonstrate how to translate arc radians based on efficiently modifying the data, however I could not find an example that achieved this and also moved the arc to a new position by canvas and also moved the radius of the arc. There may be an example, but I couldn't find one. So, this is how I managed to achieve all three transitions.

    // A helper function to convert a percentage value to radians
    var endAngleFunction = function (d) {
        return (d['percentage-value']) * ((2 * Math.PI) / 180);
    };

    // create an arc object but without setting the "endAngle"
    var arc = d3.svg.arc()
                .innerRadius(function (data) { return (d.r * 5) + 5; }) // calculating the radius based on a value within the data (different for each arc)
                .outerRadius(function (data) { return d.r * 5 })
                .startAngle(0);

    // selecting all svg paths (arcs) within arcGroup, a global variable referencing the group each arc gets appended to when created
    var arcs = arcGroup.selectAll("path")
                        .data(data);

    // transition the position of the arc to its new position
    arcs.transition("position")
        .duration(1000)
        .attr("transform", function(d) { return "translate("+ xScale(d.u) + "," + yScale(d.t) + ")"; });

    // transition the arc radians to new value 
    arcs.transition("newArc")
        .duration(1000)
        .attr("endAngle", function(d) { return endAngleFunction(d); }) // binding the new end Angle to the arc for later reference to work out what previous end angle was
        .transition()
        .delay(500)
        .duration(1000)
        .call(arcTween);

    // A function to tween the arc radians from a value to a new value - inspired by http://bl.ocks.org/mbostock/5100636
    function arcTween(transition, newAngle) {
        transition.attrTween("d", function (d) {
            var interpolate = d3.interpolate(d3.select(this).attr("endAngle"), endAngleFunction(d));
            return function (t) {
                d.endAngle = interpolate(t);
                return arc(d);
            };
        });
    }

      

Note. ... The big job for me was how to get the previous angle and go to the new angle, which I reached by associating the end angle with the arc object when creating and / or modifying, so I could then pull it out later and use it to transition.



And it seemed very good to me. However, since I'm new to D3, there might be a more efficient way to achieve this, or even structure the code or order of how things are called. From here, I'll try to tighten it up and work out a more efficient way.

I hope this can help someone at some point, as god knows I have really struggled with this for some time now.

Thanks to everyone who helped with this.

0


source







All Articles