F1refly
F1refly

Reputation: 376

Difference in chaining D3.js transitions

What needs to be done that the second transition of the green circle behaves like the second transition of the blue circle?

I would have expected that the transitions of both circles behave the same. However it seems that the first transition of the green circle is applied in place of the second transition.

const svg = d3.select("svg");

const blueCircle = svg.append("circle")
  .attr("cx", 10)
  .attr("cy", 10)
  .attr("r", 5)
  .style("fill", "blue");

const greenCircle = svg.append("circle")
  .attr("cx", 10)
  .attr("cy", 30)
  .attr("r", 5)
  .style("fill", "green");

blueCircle
  .transition()
    .duration(4000)
    .ease(d3.easeLinear)
    .attr("cx", 100)
  .transition()
    .duration(2000)
    .ease(d3.easeElastic)
    .attr("cx", 200);

const firstTransition = d3.transition()
  .duration(4000)
  .ease(d3.easeLinear);

const secondTransition = d3.transition()
  .duration(2000)
  .ease(d3.easeElastic);

greenCircle
  .transition(firstTransition)
    .attr("cx", 100)
  .transition(secondTransition)
    .attr("cx", 200);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.14.2/d3.min.js"></script>
<svg width="250" height="50"></svg>

Update

Thanks to Coola's answer and this question, I found a possibility to make the second transition of the green circle work as expected:

const greenCircle = svg.append("circle")
  .attr("class", "green")
  .attr("cx", 10)
  .attr("cy", 30)
  .attr("r", 5)
  .style("fill", "green");

const firstTransition = d3.transition()
  .duration(4000)
  .ease(d3.easeLinear);

const secondTransition = firstTransition.transition()
  .duration(2000)
  .ease(d3.easeElastic);

firstTransition
  .select("circle.green")
  .attr("cx", 100);

secondTransition
  .select("circle.green")
  .attr("cx", 200); 

However, this code has still the following flaws:

Does anybody know a solution without these issues, especially for the first point?

Upvotes: 3

Views: 78

Answers (1)

Coola
Coola

Reputation: 3162

In order to chain the transitions you have to use the on("end", function(){<do something>}).

You can read more about advanced control flows in the documentation.

greenCircle
  .transition(firstTransition)
    .attr("cx", 100)
    .on("end", () => {
      greenCircle.transition(secondTransition)
      .attr("cx", 200);
    });

Full Snippet:

const svg = d3.select("svg");

const blueCircle = svg.append("circle")
  .attr("cx", 10)
  .attr("cy", 10)
  .attr("r", 5)
  .style("fill", "blue");

const greenCircle = svg.append("circle")
  .attr("cx", 10)
  .attr("cy", 30)
  .attr("r", 5)
  .style("fill", "green");

blueCircle
  .transition()
    .duration(4000)
    .ease(d3.easeLinear)
    .attr("cx", 100)
  .transition()
    .duration(2000)
    .ease(d3.easeElastic)
    .attr("cx", 200);

const firstTransition = d3.transition()
  .duration(4000)
  .ease(d3.easeLinear);

const secondTransition = d3.transition()
  .duration(2000)
  .ease(d3.easeElastic);

greenCircle
  .transition(firstTransition)
    .attr("cx", 100)
    .on("end", () => {
      greenCircle.transition(secondTransition)
      .attr("cx", 200);
    })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.14.2/d3.min.js"></script>
<svg width="250" height="50"></svg>

UPDATE:

The above code solves only part of the problem. You will notice the animation is not exactly identical between the blue and green circles.

You also need to chain the firstTransition into the secondTransition const. Like:

const secondTransition = firstTransition.transition()
  .duration(2000)
  .ease(d3.easeElastic);

Full snippet:

const svg = d3.select("svg");

const blueCircle = svg.append("circle")
  .attr("cx", 10)
  .attr("cy", 10)
  .attr("r", 5)
  .style("fill", "blue");

const greenCircle = svg.append("circle")
  .attr("cx", 10)
  .attr("cy", 30)
  .attr("r", 5)
  .style("fill", "green");

blueCircle
  .transition()
    .duration(4000)
    .ease(d3.easeLinear)
    .attr("cx", 100)
  .transition()
    .duration(2000)
    .ease(d3.easeElastic)
    .attr("cx", 200);

const firstTransition = d3.transition()
  .duration(4000)
  .ease(d3.easeLinear);

const secondTransition = firstTransition.transition()
  .duration(2000)
  .ease(d3.easeElastic);

greenCircle
  .transition(firstTransition)
    .attr("cx", 100)
    .on("end", function () {
      greenCircle.transition(secondTransition)
      .attr("cx", 200);
    })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.14.2/d3.min.js"></script>
<svg width="250" height="50"></svg>

Upvotes: 1

Related Questions