tom c
tom c

Reputation: 140

Apply transition while keeping the old elements' positions

On the page I have a number of rectangles with the same class, say class one.

How do I apply a transition to all those rectangles so they move to a new position with a new class (maybe class two), but keeping those old rectangles stationary in the same position?

Could someone please correct me if I have explained it incorrectly?

For example I have these rectangles with class "start"

d3.select("svg")
  .selectAll("rect")
  .data([10,20,30,40,50])
  .enter()
  .append("rect")
  .attr("class", "start")
  .attr("x", d => d)
  .attr("y", 1)
  .attr("width", 5)
  .attr("height", 5);

These rectangles coordinates are (10, 1), (20, 1), (30, 1) ...

Then I move them

d3.selectAll("rect")
  .transition()
  .attr("y", (d, i) => i + 5 * 10); 

They will appear at the new co-ordinates (10, 50), (20, 51), (30, 52) ...

How can I make it so that the original rectangles with class start at (10, 1), (20, 1), (30, 1) ... are still there but have new rectangles at (10, 50), (20, 51), (30, 52) ... with class stop?

Upvotes: 3

Views: 105

Answers (2)

rioV8
rioV8

Reputation: 28673

There is no need to use each and a function to clone.

rectangles.clone(false)
  .transition()
  .attr("class", "stop")
  .attr("y", (d, i) => i + 5 * 10);

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

const rectangles = d3.select("svg")
  .selectAll(null)
  .data([10, 20, 30, 40, 50])
  .enter()
  .append("rect")
  .attr("class", "start")
  .attr("x", d => d)
  .attr("y", 1)
  .attr("width", 5)
  .attr("height", 5);

rectangles.clone(false)
  .transition()
  .attr("class", "stop")
  .attr("y", (d, i) => i + 5 * 10);
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>

Upvotes: 0

Gerardo Furtado
Gerardo Furtado

Reputation: 102194

As already made clear in your edit, you don't want to apply the transition to the existing elements: you want to clone them and apply the transition to their clones (or clone them before applying the transition to the original ones, which is the same...).

That being said, D3 has a pretty handy method named clone, which:

Inserts clones of the selected elements immediately following the selected elements and returns a selection of the newly added clones.

So, supposing that your selection is named rectangles (advice: always name your selections), instead of doing this...

rectangles.transition()
    .attr("class", "stop")
    .attr("y", (d, i) => i + 5 * 10);

...clone them first:

rectangles.each(cloneNodes)
    .transition()
    .attr("class", "stop")
    .attr("y", (d, i) => i + 5 * 10);

function cloneNodes() {
    d3.select(this).clone(false);
}

Here is the demo:

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

const rectangles = d3.select("svg")
  .selectAll(null)
  .data([10, 20, 30, 40, 50])
  .enter()
  .append("rect")
  .attr("class", "start")
  .attr("x", d => d)
  .attr("y", 1)
  .attr("width", 5)
  .attr("height", 5);

rectangles.each(cloneNodes)
  .transition()
  .attr("class", "stop")
  .attr("y", (d, i) => i + 5 * 10);

function cloneNodes() {
  d3.select(this).clone(false);
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>

Upvotes: 1

Related Questions