创建d3.js计时器的问题

时间:2018-03-08 02:35:04

标签: javascript animation d3.js timer

我根据奥运会运动员的滑雪次数创作了动画片。我已经将时间转换为毫秒来创建动画持续时间,如下所示:

.transition()
        .duration(function(d){
          return d.time_miliseconds / 4
        })
        .attr("cx", 720)

我现在想要在动画中显示时间。这样的事情:http://www.wsj.com/graphics/2018-winter-olympics-art-of-the-millisecond/在动画发生的时候,左上角的时间过去了。

我不完全确定如何创建此计时器。在这里,我创建了一个getTimeData函数,但我不确定如何实现它,或者这是否是正确的方法:

function getTimeData(){
              var now = new Date;
              var milliseconds = now.getMilliseconds()
              var seconds = now.getSeconds()
              var minutes = now.getMinutes()
              return {"ms": milliseconds, "seconds": seconds, "minutes": minutes}
            }

您可以在我的bl.ock上查看我的所有代码:https://bl.ocks.org/JulienAssouline/256a51554899b8619ba7918590a569f1

澄清我想以分钟,秒和毫秒显示时间。

2 个答案:

答案 0 :(得分:2)

你可以在转换开始时启动一个计时器(在这种情况下我使用d3.timer)并在转换结束时停止它。此计时器可用于显示已用时间。

多次转换

D3转换包括侦听转换开始和结束事件的能力。但是,在对多个元素的选择应用转换时,实际上是在创建多个转换 - 一个用于选择中的每个元素。因此,这些侦听器会触发正在转换的每个元素。如果一切都在同一时间开始,那么在启动转换时这不是一个问题:您可以调用一个函数来根据需要多次启动计时,但影响不大。如果在不同的持续时间内发生转换,则会出现问题:在调用函数停止计时之前,必须等到最后一次转换完成。

检测多个转换结束的一个可能选项是使用计数器来跟踪已完成的转换次数:

var counter = 0;
selection.transition()
  .on("end", function() { 
    counter++; 
    if (counter == selection.size()) { // everything is done }
  })

另一个考虑因素是多个转换不会同时启动,即使计划立即启动 - 浏览器将快速连续启动,但如果处理毫秒则不会同时启动。

d3计时器&精度

让计时器继续进行相对简单,

var timer = d3.timer(function(t) { // callback function
  if (t > 5000) timer.stop();  // run for 5 seconds
  d3.select("p").html(t);       // update some text
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<p></p>

但是,请记住,计时器不会连续运行,而是反复运行。它不会在每个可能的时刻触发回调 - 这将是无限次的操作 - 而是尽可能快地触发,这将根据环境而变化。正如d3文档所述,d3.timer

  

计划新计时器,重复调用指定的回调   直到计时器停止。 (link

这就是为什么计时器可能超过上述片段的总时间。

示例

多次转换将在稍微不同的时间开始 - 通常这些可能是不可察觉的,但是使用计时器指示何时完成所有操作,转换多个元素可能导致显示时间错误。

此外,即使转换的单个元素仍然会导致重复,而不是连续的转换评估,因此您可能报告的时间超过或低于您设置的预定最长持续时间。

下面的示例应该证明这一点,转换计划在10秒后结束(10个元素,duration = i*1000+1000,最大i为9),但绘制的时间可能与高达不一致处理毫秒时的精确度:

var width = 600;
var height = 500;

var svg = d3.select("body").append("svg")
  .attr("width",width)
  .attr("height",height);
  
var text = svg.append("text")
  .attr("x", 50)
  .attr("y", 40);
  
var circles = svg.selectAll("circle")
  .data(d3.range(10))
  .enter()
  .append("circle")
  .attr("cx", 30)
  .attr("cy", function(d) { return d * 15 + 45; })
  .attr("r",6);
  
var completed = 0;
var n = circles.size(); 

circles.transition()
  .on("start", function(d,i) { if(i ==0) startTimer();  })
  .on("end", function() { completed++ })
  .attr("cx", width-30)
  .duration(function(d) { return d*1000+1000; })

var format = d3.timeFormat("%S.%L");

function startTimer() {
  var timer = d3.timer(function(t) { 
    if (completed >= n) timer.stop();
    text.text("milliseconds:" + format(t));
  });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

<强>精密

计算机不一定会有几毫秒的时间,这取决于你如何扩展数据这可能是一个问题 - 如果你放慢速度,那么你可以轻松地解决这个错误。如果你是实时展示的东西,也许不是。

如果实时显示转换,则视觉上的毫秒差异难以辨认,唯一的问题是计时器显示的运行时间与预期不同。为了解决这个问题,我们可以使用稍微不同的方法:

  • 将计时器设置为在第一次转换时开始(如上所述)
  • 在最长持续时间结束后停止计时器(预先cacluate)
  • 如果计时器已停止,则将显示时间的文本设置为最大持续时间,以便它告诉您最长持续时间,而不是实际停止时间:

var width = 600;
var height = 300;

var svg = d3.select("body").append("svg")
  .attr("width",width)
  .attr("height",height);
  
var text = svg.append("text")
  .attr("x", 50)
  .attr("y", 40);
  
var circles = svg.selectAll("circle")
  .data(d3.range(10))
  .enter()
  .append("circle")
  .attr("cx", 30)
  .attr("cy", function(d) { return d * 15 + 45; })
  .attr("r",6);
  
var maxTime = d3.max(circles.data(), function(d) {
  return d * 1000 + 1000;  
})

circles.transition()
  .on("start", function(d,i) { if(i==0) startTimer() })
  .attr("cx", width-30)
  .duration(function(d) { return d*1000+1000; })

var format = d3.timeFormat("%S.%L");

function startTimer() {
  var timer = d3.timer(function(t) { 
    if (t > maxTime) {
      timer.stop();
      text.text("milliseconds" + format(maxTime));  // ensure final time is right.
    }
    else {
      text.text("milliseconds:" + format(t));      
    }
  });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

要格式化数字,我建议你只使用d3数字格式。以上两个片段(不是第一个)使用基本格式,但这很容易适应。

答案 1 :(得分:2)

优秀Andrew's answer的替代方案是获得最长时间......

var maxTime = d3.max(data, function(d) {
    return d.time_miliseconds
});

...并使用attrTween打印文本元素:

textSelection.transition()
    .duration(maxTime / 4)
    .attrTween("text", function(d) {
        var that = this
        var i = d3.interpolateNumber(0, maxTime);
        return function(t) {
            return d3.select(that).text("Time: " + i(t));
        }
    });

注意这样一个事实:在这种方法中,选定的d3.ease会改变文本更改的速率。

以下是您修改过的bl.ocks:https://bl.ocks.org/anonymous/7bdaf9f1a6a0123f83229a92b42460a0/3cfd14c76080ac2461ab91ce98a3bbe253ba8765