Как перезапустить (восстановить) приостановленный таймер в анимации холста, используя d3 - PullRequest
0 голосов
/ 31 октября 2018

Я построил следующий пример игрушки (jsfiddle здесь ; пожалуйста, нажмите кнопку остановки в течение 10 секунд):

var width = 750, height = 400;
var canvas = d3.select("body")
  .append("canvas")
  .attr("width", width)
  .attr("height", height);
var context = canvas.node().getContext("2d");   
var x=Math.random()*50;
var y=Math.random()*50;

d3.select("body")
.append("button")
.html("stop")
.on("click",function(){ti.stop()});
d3.select("body")
.append("button")
.html("restart")
.on("click",function(){ti.restart()});

var ti = d3.timer(function(elapsed) {
context.clearRect(0, 0, width, height); // Clear the canvas.
context.fillStyle="red";
var t=Math.min(1,elapsed/10000)
context.fillRect(x*(1-t)+width*t,y*(1-t)+height*t,10,10);
})  

Мне нужно перезапустить анимацию с того места, где я приостановил ее после нажатия кнопки «Стоп». Мой код выдает ошибку при нажатии кнопки перезагрузки. В любом случае, в другом, более громоздком коде команда time.restart() тоже не работает, так что я думаю, что не понял, как в D3 таймеры могут быть приостановлены / перезапущены.

Ответы [ 2 ]

0 голосов
/ 01 ноября 2018

timer.restart() не так полезен, как вы думаете. Из документов :

таймер . перезапуск ( обратный вызов [, задержка [, время ]])
Перезапустите таймер с указанным обратным вызовом и дополнительными задержкой и временем. Это эквивалентно остановке этого таймера и созданию нового таймера с указанными аргументами, хотя этот таймер сохраняет первоначальный приоритет вызова.

Так что это не сделает работу за вас. Он просто избавляет вас от создания нового таймера и (в случае нескольких таймеров) сохраняет свой приоритет.

Также, как видно из определения функции, параметр callback является обязательным. Вот почему вы получаете TypeError: callback is not a function в консоли.

По сути, вы должны преобразовать свою анонимную функцию обратного вызова в именованную функцию и передать ее как конструктору, так и методу restart. Но если вы попытаетесь это сделать, вы увидите, что перезагрузка означает перезагрузку: она начинается с начала.

Итак, вы не можете рассчитывать на elapsed, чтобы рассчитать положение вашей анимации, вам нужно посчитать время самостоятельно. Установите время запуска непосредственно перед созданием таймера, используя d3.now() минус время, в течение которого анимация уже запущена (т. Е. 0 при первом вызове).

var totalElapsedTime = 0;
var startTime = d3.now() - totalElapsedTime;
var t = d3.timer(myCallback);

Затем внутри обратного вызова вычислите прошедшее время:

var elapsedTime = d3.now() - startTime;
// This is how d3.js calculates `elapsed`.
// The actual code is more complicated but the result is the same.

Запишите прошедшее время в обработчике события click кнопки stop:

on("click",function(){
    totalElapsedTime = d3.now() - startTime; 
    ti.stop();
});

Обновление startTime в обработчике событий click кнопки restart:

.on("click",function(){
    startTime = d3.now() - totalElapsedTime;
    ti.restart(myCallback);
});

И все готово.

Для записи, реальная функциональность паузы / возобновления была предложена на GitHub , но была отклонена. Другой разработчик разработал проект для его реализации на случай, если вы заинтересованы.

Примечание : в приведенном ниже фрагменте кода я немного упростил код и уменьшил размер холста, чтобы было проще отслеживать анимацию в окне фрагмента.

var width = 300,
    height = 150;
var canvas = d3.select("body")
  .append("canvas")
  .attr("width", width)
  .attr("height", height);
var context = canvas.node().getContext("2d");

d3.select("body")
  .append("button")
  .html("stop")
  .on("click", function() {
    totalElapsedTime = d3.now() - startTime;
    ti.stop();
  });
d3.select("body")
  .append("button")
  .html("restart")
  .on("click", function() {
    startTime = d3.now() - totalElapsedTime;
    ti.restart(myCallback);
  });

var startTime = d3.now();
var totalElapsedTime = 0;
var ti = d3.timer(myCallback);

function myCallback() {
  var elapsedTime = d3.now() - startTime;
  context.clearRect(0, 0, width, height);
  context.fillStyle = "red";
  var t = Math.min(1, elapsedTime / 10000)
  context.fillRect( width * t, height * t, 10, 10);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
0 голосов
/ 01 ноября 2018

Вы должны следить за временем, когда вы останавливаете таймер. И отрегулируйте в обратном вызове прошедшее время.

var width = 750, height = 400;
var canvas = d3.select("body")
  .append("canvas")
  .attr("width", width)
  .attr("height", height);
var context = canvas.node().getContext("2d");	
var x=Math.random()*50;
var y=Math.random()*50;

d3.select("body")
  .append("button")
  .html("stop")
  .on("click",function(){t.stop(); alreadyElapsed = stopAt;});
d3.select("body")
  .append("button")
  .html("restart")
  .on("click",function(){t = createTimer();});

var t = createTimer();
var stopAt;
var alreadyElapsed = 0;

function createTimer() {
  return d3.timer(function(elapsed) {
    elapsed += alreadyElapsed;
    stopAt = elapsed;
    context.clearRect(0, 0, width, height); // Clear the canvas.
    context.fillStyle="red";
    var t=Math.min(1,elapsed/10000)
    context.fillRect(x*(1-t)+width*t,y*(1-t)+height*t,10,10);
  });
}
rect {
  fill:steelblue;
}
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...