Search code examples
javascriptd3.jstransitiontimelinetween

D3.js: Retrieve x-axis information while transition is running


I am trying to create a timeline using D3.js v4. I have successfully created my scatter plot with its axis and a brush to allow users define a specific time period.

I want to allow users to be able to 'play' the timeline just like an audio/video player having an indicator that will move from left to right using a customizable duration. In order to achieve this I've placed a vertical line with a transition to act as the indicator.

My problem is that I cannot retrieve the x-axis coordinates while the transition is running. I want to achieve this because the x-axis values need to interact with another part of the code.

I've tried everything including playing around with tween and attrTween functions but I couldn't make it work. Ideally I would like the indicator to begin and stop within the brush limits.

svg.append("g")
    .attr("class", "brush")
    .call(brush)
    .call(brush.move, x.range());

svg.append('line')
    .attr("class", "timeline-indicator")
    .attr("stroke-width", 2)
    .attr("stroke", "black")
    .attr("x1", 0)
    .attr("y1", 0)
    .attr("x2", 0)
    .attr("y2", height)
    .transition()
        .duration(9000)
        .attr("x1", 500)
        .attr("y1", 0)
        .attr("x2", 500)
        .attr("y2", height);

Solution

  • You should be able to accomplish this using a tween function in your transition. A tween function will trigger on every tick of the transition and is one way to call a function every tick.

    The tween method needs an attribute name (as it is intended to provide custom interpolation for an attribute), but this can be a dummy attribute, or one that isn't changed (as in my example below). Documentation on the method is here.

    In my example I pull the x property (well, cx property) of a circle as it moves across the screen, using a tween function:

     .tween("attr.fill", function() {
            var node = this;
            return function(t) { 
             console.log(node.getAttribute("cx"));
            }
          })
    

    Here is a snippet of it at work:

    var svg = d3.select("body").append("svg")
      .attr("width",400)
      .attr("height",400);
      
    var circle = svg.append("circle")
      .attr("cx",20)
      .attr("cy",20)
      .attr("r",10);
     
    circle.transition()
      .attr("cx",380)
      .attr("cy",20)
      .tween("attr.fill", function() {
        var node = this;
        return function(t) { 
         console.log(node.getAttribute("cx"));
        }
      })
      .duration(1000);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>