Skip to content Skip to sidebar Skip to footer

What Attributes Does D3 Transition Change?

I am trying to understand what exactly gets transition in d3. For example var bars = svg.selectAll(null) .data(my_values) .enter() .append('rect') // statement before transit

Solution 1:

Your second question...

  1. How does multiple transition work?

Is quite simple to answer. Here, the API is clear:

transition.transition()

Returns a new transition on the same selected elements as this transition, scheduled to start when this transition ends.

So, the new transition will start when the previous one finishes, and so on. Let's see it:

var rect = d3.select("svg").append("rect")
  .attr("width", 100)
  .attr("height", 0)
  .style("fill", "black")
  .transition()
  .duration(1000)
  .attr("height", 100)
  .transition()
  .duration(1000)
  .style("fill", "teal")
<scriptsrc="https://d3js.org/d3.v4.min.js"></script><svg></svg>

The interesting question

However, the interesting question here is your first one:

  1. Does the transition effect any statement before the transition or does the transition effect only statement after the transition?

Well, this is also quite basic: the transition() method creates a transition between a starting value and a target value. You have to set the attribute before the transition (the starting value) and after the transition (the target value).

The problem is that attributes not previously set will have a null value.

Let's see it: without specifying the height of the rectangle, it is null:

var rect = d3.select("svg").append("rect");

console.log(rect.attr("height"))
<scriptsrc="https://d3js.org/d3.v4.min.js"></script><svg></svg>

Therefore, there is no smooth transition regarding the height if you don't set it before the transition:

var rect = d3.select("svg").append("rect")
  .attr("width", 100)
  .transition()
  .duration(1000)
  .attr("height", 100);
<scriptsrc="https://d3js.org/d3.v4.min.js"></script><svg></svg>

And here comes the interesting point: I wrote an answer (now deleted, users with >10k can see it) explaining that the interpolator cannot interpolate from null to a given value. However, it can interpolate:

var interpolator = d3.interpolateNumber(null, 100);
d3.range(0,1.1,.1).forEach(function(d){
	console.log(interpolator(d))
});
<scriptsrc="https://d3js.org/d3.v4.min.js"></script>

So, what is happening here is a bit more complicated. Using interpolateNumber we can interpolate from null to a given value. Look at this demo, where the height was not set:

var rect = d3.select("svg").append("rect")
  .attr("width", 100)
  .transition()
  .duration(1000)
  .attrTween("height", function() {
    return d3.interpolateNumber(d3.select(this).attr("height"), 100);
  });
<scriptsrc="https://d3js.org/d3.v4.min.js"></script><svg></svg>

Why the default transition.attr() doesn't work when you don't set the attribute before the transition?

After some investigation, it seems to me that transition() is converting the numeric target value to a string (like 100 to "100"). This is very strange, because the API explicitly says that...

... an interpolator is chosen based on the type of the target value, using the following algorithm:

  1. If value is a number, use interpolateNumber.
  2. If value is a color or a string coercible to a color, use interpolateRgb.
  3. Use interpolateString.

Here is the source code, look at the last line:

exportdefaultfunction(name, value) {
    var fullname = namespace(name),
        i = fullname === "transform" ? interpolateTransform : interpolate;
    returnthis.attrTween(name, typeof value === "function" 
        ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, tweenValue(this, "attr." + name, value)) 
        : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname) 
        : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value + ""));
        //converting to string here ------------------------------------------^
}

As we can see, it's coercing the value to a string. Therefore, the transition will not work because it will use interpolateString instead of interpolateNumber:

var interpolator = d3.interpolateString(null, "100");
d3.range(0,1.1,.1).forEach(function(d){
	console.log(interpolator(d))
});
<scriptsrc="https://d3js.org/d3.v4.min.js"></script>

Conclusion

transition() creates a transition from a starting value to a target value. If the starting value is not set, it will default to null. However, as transition() uses interpolateString, the interpolation will not work properly.

Update in 18/11/2017

According to Mike Bostock (D3 creator), the source code is correct (and, therefore, the documentation is wrong). As he says in this GitHub issue:

interpolateString (or interpolateRgb) is the correct behavior here. interpolateNumber should never be used for attributes.

The explanation is that values like "100px" or "1.3em" would not work with interpolateNumber. So, again, don't rely on null starting values: always set the attribute before and after the transition().

Thanks to @altocumulus for commenting on the GitHub issue.

Solution 2:

Transition is one of the important feature in graphical representation of any data with D3JS. Transition is a process of changing from one state to another. This transition can be used in style and attribute methods and this cannot be used in the append and data methods. We can also delay function which is used to pause the flow for selected time and duration function can specify the time of transition.

<body><h3>Demo Sample</h3><script>
     d3.select("body")
       .transition()
       .style("background-color", "black")
       .duration(3000);
  </script>

Multiple Transition are also possible. First It will execute the first mentioned transition and then followed by next transition. This is the way of working multiple transition in D3JS.

Post a Comment for "What Attributes Does D3 Transition Change?"