Hans-Peter Stricker
Hans-Peter Stricker

Reputation: 373

How to add conditional transitions?

This question is about transitions with d3.js, e.g. the change of the x attribute of a rectangle.

If you want the change to occur immediately you may set delay = duration = 0 as in

svg.selectAll("rect")
   .transition()
   .delay(delay)
   .duration(duration)
   .attr("x", 0)

but this may be too expensive (assuming that transitions are more CPU consumpting than immediate changes). So how can I add the transition conditionally, behaving like

if (delay == 0 && duration == 0) {
    svg.selectAll("rect")
        .attr("x", 0)
} else {
    svg.selectAll("rect")
        .transition()
        .delay(delay)
        .duration(duration)
        .attr("x", 0)
}

but less redundant and more compact, somehow like x = condition ? 0 : 1?

Upvotes: 4

Views: 714

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102219

This is a very strange situation, and I cannot think about a use case where it would be useful. That being said, here is one out of many possible solutions: extend the D3 selection prototype.

In my approach, we'll extend the prototype to return the exact same selection in case of both delay and duration are zero, or return a transition selection based on the current selection if they're not zero:

d3.selection.prototype.conditionalTransition =
  function(cond1, cond2) {
    return cond1 && cond2 ? this.transition().duration(duration).delay(delay) : this;
  };

Then, you just continue the chain of attr() methods:

d3.selectAll("rect")
  .conditionalTransition(delay, duration)
  .attr("x", 200);

Finally, pay attention to the fact that regular selections and transition selections have different methods with the same name, like selection.on() and transition.on().

Here is the first demo. All the rects are in x = 10. The selection is turned into a transition selection:

d3.selection.prototype.conditionalTransition =
  function(cond1, cond2) {
    return cond1 && cond2 ? this.transition().duration(duration).delay(delay) : this;
  };

const rect = d3.selectAll("rect");

const delay = 100,
  duration = 1000;

rect.conditionalTransition(delay, duration)
  .attr("x", 200)

function transitioning(sel) {
  return sel.transition()
    .delay(delay)
    .duration(duration);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg>
 <rect x="10" y="10" width="50" height="20" fill="teal"></rect>
 <rect x="10" y="40" width="50" height="20" fill="teal"></rect>
 <rect x="10" y="70" width="50" height="20" fill="teal"></rect>
 <rect x="10" y="100" width="50" height="20" fill="teal"></rect>
</svg>

And here is the same code, with both delay and duration equal to zero. As you can see the rectangles are already in the final position, there is no transition:

d3.selection.prototype.conditionalTransition =
  function(cond1, cond2) {
    return cond1 && cond2 ? this.transition().duration(duration).delay(delay) : this;
  };

const rect = d3.selectAll("rect");

const delay = 0,
  duration = 0;

rect.conditionalTransition(delay, duration)
  .attr("x", 200)

function transitioning(sel) {
  return sel.transition()
    .delay(delay)
    .duration(duration);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg>
 <rect x="10" y="10" width="50" height="20" fill="teal"></rect>
 <rect x="10" y="40" width="50" height="20" fill="teal"></rect>
 <rect x="10" y="70" width="50" height="20" fill="teal"></rect>
 <rect x="10" y="100" width="50" height="20" fill="teal"></rect>
</svg>

Upvotes: 6

Related Questions