Pausing and resuming a transition

2020-08-20 08:34发布

问题:

I am using setInterval, so transitions take place after a certain interval. Is it possible to get pausing and resuming to work with setInterval?

Any suggestions/pointers in the right direction will be really helpful.

回答1:

This question was posted when D3 v3 was the latest version available. 5 years later D3 v5 has some new methods, like selection.interrupt(), transition.on("interrupt"...) and local variables, which can make the task more simple and less painful.

So, let's suppose a simple cx transition on a circle:

const svg = d3.select("svg");
const circle = svg.append("circle")
  .attr("r", 15)
  .attr("cx", 20)
  .attr("cy", 50)
  .style("fill", "teal")
  .style("stroke", "black");
circle.transition()
  .duration(10000)
  .ease(d3.easeLinear)
  .attr("cx", 580);
svg {
  background-color: wheat;
  display: block;
};
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="600" height="100"></svg>

The idea is interrupting the transition when, for instance, a button is pressed:

selection.interrupt();

And then, with a local variable, use the listener for interrupt to get the current position:

.on("interrupt", function() {
    local.set(this, +d3.select(this).attr("cx"))
}); 

Finally, when the button is pressed again, we use local.get(this) and a simple math to get the remaining duration.

It's also worth mentioning that this works for linear easing; if you have another easing, like the default d3.easeCubic, you'll need a way more complex code.

And here is the demo:

const svg = d3.select("svg");
const local = d3.local();
const button = d3.select("button");
const circle = svg.append("circle")
  .attr("r", 15)
  .attr("cx", 20)
  .attr("cy", 50)
  .style("fill", "teal")
  .style("stroke", "black");
circle.transition()
  .duration(10000)
  .ease(d3.easeLinear)
  .attr("cx", 580)
  .on("interrupt", function() {
    local.set(this, +d3.select(this).attr("cx"))
  });
button.on("click", function() {
  if (d3.active(circle.node())) {
    circle.interrupt();
    this.textContent = "Resume";
  } else {
    circle.transition()
      .ease(d3.easeLinear)
      .duration(function() {
        return 10000 * (560 - local.get(this)) / 560;
      })
      .attr("cx", 580)
    this.textContent = "Stop";
  }
})
svg {
  background-color: wheat;
  display: block;
};
<script src="https://d3js.org/d3.v5.min.js"></script>
<button>Stop</button>
<svg width="600" height="100"></svg>