您可以让自己成为此类事情的简单跑步者。它基本上是一个游戏引擎,你需要一个游戏循环:)
这是一个天真的例子。在此处跳过大部分错误检查和验证以进行酿造。
class TelemetryPlayer {
constructor(items, render, interval) {
// setup local data.
this.interval = interval;
this.items = items;
this.render = render;
this.startLoop();
}
// Loop simply initializes before the first run, and then runs the loop.
// Other places do the actual work.
startLoop() {
this._renderInProgress = false;
this._currentIndex = 0;
// save the interval reference if you wanna pause/reset later on.
this._interval = setInterval(this.doWork.bind(this), this.interval);
}
// here we perform the actual render.
doWork() {
if (this._renderInProgress) {
// previous render has not completed yet.
console.log('skip');
return;
}
console.log('Tick');
this._renderInProgress = true;
const item = this.items[this._currentIndex];
console.log('Tick');
// now, call your renderer, and update stuff when complete.
this.render(item)
.then(() => {
// Or this can be a callback or similar.
this._renderInProgress = false;
// Ready next item. Do not go out of array bounds.
this._currentIndex++;
if (this._currentIndex === this.items.length) {
this._currentIndex = 0;
}
});
}
// You can then add fun things like skip, pause, reset etc.
skip(item) {
if (item < 0 || item > this.items.length) {
return;
}
// pause first
this.pause();
this._currentIndex = item;
this.unpause();
}
//
reset() {
this.skip(0);
}
//
pause() {
this._interval = clearInterval(this._interval);
}
unpause() {
if (!this._interval) {
this._interval = setInterval(this.doWork.bind(this), this.interval);
}
}
// you can even add items later
addItem(item) {
this.items.push(item);
}
// or replace them.
replaceItem(item, index) {
this.items[index] = item;
// show the new item right away.
this.skip(index);
}
// or add an item to be played just once.
playOnce(item) {
this.pause();
this.render(item);
this.unpause();
}
}
现在这是一个示例用法。您可以复制代码(上面的类和下面的代码块)并将其粘贴到 StackOverflow 上的控制台中,以查看它的工作情况。你可能想做其他事情,但你会明白要点。
let items = [ 100, 200, 300, 50, 100, 200, 300, 250 ];
const timeline = document.createElement('ul');
// maybe better do this by adding class and external stylesheet
timeline.setAttribute('style', 'padding: 15px; border: 1px solid gray; list-style-type: none; height: 500px; width: 100%; position: absolute; top: 0;overflow-y: scroll;')
document.body.appendChild(timeline);
let render = function(item) {
return new Promise(function (resolve) {
// run something (in) expensive with item now.
const li = document.createElement('li');
// again, better do this with class.
li.setAttribute('style', `display: inline-block; width: 25px; margin: 5px; background-color: #c1c1c1; height: ${item}px;`);
timeline.appendChild(li);
li.scrollIntoView();
// return when done.
resolve();
});
}
const player = new TelemetryPlayer(items, render, 1500);
// now you can do things like speed up etc.
// now you can do things like speed up etc.
function speedUp() {
// speedup 3 seconds after "playback" starts.
return new Promise((resolve) => setTimeout(() => {
player.pause();
player.interval = 600;
player.unpause();
resolve();
}, 3000));
}
function playOnce() {
// add item once, but not in the array we loop around in
return new Promise((resolve) => setTimeout(() => {
player.playOnce(1000);
resolve();
}, 3000));
}
// or add a few items that will be repeated.
function addItems() {
// add a super very high item 3 seconds after function call.
return new Promise((resolve) => setTimeout(() => {
player.pause();
player.addItem(400);
player.addItem(430);
player.addItem(420);
player.unpause();
// now rewind to this block. I am off by one, likely.
player.skipTo(player.items.length - 3);
resolve();
}, 5000))
}
speedUp()
.then(playOnce)
.then(addItems);