简体   繁体   中英

First d3 transition on update inside interval is not transitioning

I have a donut chart that is being used as a way to show progression. I don't have a way to show you the donut chart, but the code is simple enough to copy and paste.

I added the code to show you an example. I've tried various unreasonable methods to make the transition work the first time. But for some reason it's still not working. All examples online are pretty similar so I'm not really sure why this is happening.

 var data = [95, 5]; var pie = d3.pie().sort(null); var svg = d3.select("svg"), width = svg.attr("width"), height = svg.attr("height"), radius = Math.min(width, height) / 2; var arc = d3.arc().innerRadius(60).outerRadius(radius); function createdonut() { g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); //Inner SVG Circle svg.append("svg:circle").attr("cx", width / 2).attr("cy", height / 2).attr("r", 60).style("fill", "#ead4d4").append("g"); var color = d3.scaleOrdinal(['#4daf4a', '#377eb8', '#ff7f00', '#984ea3', '#e41a1c']); //Generate groups var arcs = g.selectAll("arc").data(pie(data)).enter().append("g").attr("class", "arc") //Draw arc paths arcs.append("path").attr("fill", function (d, i) { return color(i); }).attr("d", arc).on('mouseover', mouseover); function mouseover(d, i) { $('#percentage').html(i.data + ' units'); } } function updateDoNut(update) { data[0] = data[0] - update; data[1] = data[1] + update; var path = d3.select("svg").selectAll("path").data(pie(data)); /*path.enter().append("path").attr("fill", function (d, i) { return color[i]; }).attr("d", arc);*/ path.transition().duration(100).attrTween("d", arcTween); } function arcTween(a) { var i = d3.interpolate(this._current, a); this._current = i(0); return function (t) { return arc(i(t)); }; } createdonut(); //updateDoNut(0); var inter = setInterval(function () { updateDoNut(5); }, 3000);
 <script src="https://d3js.org/d3.v6.min.js"></script> <div id="my_dataviz"> <svg width="300" height="200"> </svg> <div id="percentage">0 units</div> </div>

If we look at your tween function we'll see a problem:

    var i = d3.interpolate(this._current, a);
    this._current = i(0);

this.current is undefined when you first start transtioning - so how is D3 to interpolate between undefined and an object contianing arc properties? It doesn't. Resulting in the non-transition you are seeing. Set this._current when appending the arcs:

 arcs.append("path")
     .attr("fill", function (d, i) {
        return color(i);
     })
     .attr("d", arc)     
     .each(function(d) {
          this._current = d;
        })
     .on('mouseover', mouseover);

Now when you update the circle, there is a valid start point for the interpolator and you should see a transition:

 var data = [95, 5]; var pie = d3.pie().sort(null); var svg = d3.select("svg"), width = svg.attr("width"), height = svg.attr("height"), radius = Math.min(width, height) / 2; var arc = d3.arc().innerRadius(60).outerRadius(radius); function createdonut() { g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); //Inner SVG Circle svg.append("svg:circle").attr("cx", width / 2).attr("cy", height / 2).attr("r", 60).style("fill", "#ead4d4").append("g"); var color = d3.scaleOrdinal(['#4daf4a', '#377eb8', '#ff7f00', '#984ea3', '#e41a1c']); //Generate groups var arcs = g.selectAll("arc").data(pie(data)).enter().append("g").attr("class", "arc") //Draw arc paths arcs.append("path").attr("fill", function (d, i) { return color(i); }).attr("d", arc).each(function(d) { this._current = d; }).on('mouseover', mouseover); function mouseover(d, i) { $('#percentage').html(i.data + ' units'); } } function updateDoNut(update) { data[0] = data[0] - update; data[1] = data[1] + update; var path = d3.select("svg").selectAll("path").data(pie(data)); path.transition().duration(2000).attrTween("d", arcTween); } function arcTween(a) { var i = d3.interpolate(this._current, a); this._current = i(0); return function (t) { return arc(i(t)); }; } createdonut(); //updateDoNut(0); var inter = setInterval(function () { updateDoNut(5); }, 3000);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://d3js.org/d3.v6.min.js"></script> <div id="my_dataviz"> <svg width="300" height="200"> </svg> <div id="percentage">0 units</div> </div>

Why doesn't this interpolation between undefined and an object generate an error? Well D3-interpolate will try to interpolate very hard. In this case, between undefined and an object, it'll use d3-interpolateObject, which will interpolate as follows:

For each property in b, if there exists a corresponding property in a, a generic interpolator is created for the two elements using interpolate. If there is no such property, the static value from b is used in the template. ( docs )

So, as there are no properties in undefined, the interpolator just uses a static value for every point in the interpolation, hence the lack of a transition on the first update: every interpolated point is the same: the end point values.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM