Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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);
like image 637
user3389819 Avatar asked Dec 01 '25 05:12

user3389819


2 Answers

Andrew's answer is a nice one and probably the traditional way to do it.

However, just for the sake of curiosity, here is an alternative answer using d3.timer.

The math is, of course, a bit more complicated. Also, have in mind that the elapsed time is apparent:

The exact values may vary depending on your JavaScript runtime and what else your computer is doing. Note that the first elapsed time is 3ms: this is the elapsed time since the timer started, not since the timer was scheduled.

Check the demo:

var svg = d3.select("body")
  .append("svg")
  .attr("width", 500)
  .attr("height", 100);

var circle = svg.append("circle")
  .attr("cx", 30)
  .attr("cy", 50)
  .attr("r", 20)
  .attr("fill", "tan")
  .attr("stroke", "dimgray");

var timer = d3.timer(move);

function move(t) {
  if (t > 2000) timer.stop();
  console.log("position now is: " + ~~(30 + (t / 2000) * 440))
  circle.attr("cx", 30 + (t / 2000) * 440)
}
.as-console-wrapper { max-height: 30% !important;}
<script src="https://d3js.org/d3.v4.min.js"></script>
like image 193
Gerardo Furtado Avatar answered Dec 02 '25 19:12

Gerardo Furtado


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>
like image 39
Andrew Reid Avatar answered Dec 02 '25 18:12

Andrew Reid



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!