How to properly repeat nested transitions in d3?

Currently, the code below moves the squares in a wavy manner, but I want to change it to have one traveling wave (like in stadiums). I guess I need to figure out how to add a delay to the repeat loop so that the first square goes down and stays on until the last square starts going up.

enter image description here

Here's my code and jsfiddle :

var margin = {top: 40, bottom: 40, left: 40, right: 40},
    width = 960 - margin.left - margin.right,
    height = 200 - margin.bottom - margin.top;

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var n = 20,
    rect_width = 20,
    padding = 1, 
    speed = 1000,
    item_delay = 40;

var x = d3.scale.ordinal()
    .domain(d3.range(n))
    .rangePoints([0, n * (rect_width + padding)]);

var rects = svg.selectAll(".rect")
    .data(x.domain())
  .enter().append("rect")
    .attr("x", function(d,i){ return x(i); })
    .attr("y", 0)
    .attr("height", rect_width)
    .attr("width", rect_width)
    .attr("class", "rect")
  .transition() 
    .duration(speed)
    .delay(function(d,i){ return i * 120; })
    .each(start_repeat);

function start_repeat(){
    var rect = d3.select(this);
    (function repeat(){
        rect = rect.transition()
            .attr("y", 0)
          .transition()
            .attr("y", 100)
            .each("end", repeat);    
    })()
}

      

I think my problem is that I don't really understand what's going on inside the function repeat

.

UPDATE

I figured out a simple loop for the transition, but it requires me to manually calculate the loop duration, and I would like to avoid that (see snippet for live version).

speed = 500;
var loop_duration = 2000;
(function loop(){
rects.transition()
    .delay(function(d,i){ return i * item_delay; })
    .duration(speed)
    .attr("y", 100) // down
  .transition()
    .attr("y", 0)
setTimeout(loop, loop_duration)
})()    

      

var margin = {top: 40, bottom: 40, left: 40, right: 40},
    width = 960 - margin.left - margin.right,
    height = 300 - margin.bottom - margin.top;

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var n = 20,
    rect_width = 20,
    padding = 1, 
    speed = 500,
    item_delay = 100;

var x = d3.scale.ordinal()
    .domain(d3.range(n))
    .rangePoints([0, n * (rect_width + padding)]);

var rects = svg.selectAll(".rect")
    .data(x.domain())
  .enter().append("rect")
    .attr("x", function(d,i){ return x(i); })
    .attr("height", rect_width)
    .attr("width", rect_width)
    .attr("class", "rect")
    .attr("y", 0); // up - very important, if you don't set this, there is no interpolation

var loop_duration = 2000;
(function loop(){
    rects.transition()
        .delay(function(d,i){ return i * item_delay; })
        .duration(speed)
        .attr("y", 100) // down
      .transition()
        .attr("y", 0)
    setTimeout(loop, loop_duration)
})()
      

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
      

Run codeHide result


I tried to split the function start_repeat

to understand what's going on, but I still don't get it. For example, what's the difference between old_rect

andrect

?

function start_repeat() {
  var rect = d3.select(this);
  (function repeat() {
    var move_down_t0 = rect.transition() // chain the down-transition on the same selection
        .attr("y", 100);

     var move_up_t1 = move_down_t0.transition()
        .attr("y", 0)

     old_rect = rect;
     rect = move_up_t1.each("end", repeat);
  })();
}

      

+3


source to share


2 answers


I'm a little confused, but I tried to set the duration and delays equal so that the transition lasts as long as the values ​​are set.

Try the following:



var rects = svg.selectAll(".rect")
    .data(x.domain())
  .enter().append("rect")
    .attr("x", function(d,i){ return x(i); })
    .attr("y", 0)
    .attr("height", rect_width)
    .attr("width", rect_width)
    .attr("class", "rect")
  .transition() 
    .duration(speed)
    .delay(function(d,i){ return i * (speed/(n*2)); })
    .each(start_repeat);

      

+1


source


My decision:

http://jsfiddle.net/k5zm18hn/2/



function repeat(){
    if(d3.select(this).attr("y")==0){
        d3.select(this).transition()
            .delay(pausing_delay)
            .duration(speed)
            .attr("y", 100)
            .each("end", repeat);
    }
    else{
        d3.select(this).transition()
            .attr("y", 0)
            .duration(speed)
            .each("end", repeat);
    }
}

      

Modify the pausing_delay variable for optimal results.

+1


source







All Articles