Цикл массива с определенной скоростью в Javascript
Я новичок здесь, так что избавьте меня, пожалуйста.
Я работаю над небольшим моим хобби-проектом, в котором приложение собирает телеметрию, которая записывается и сохраняется на частоте 60 Гц (так, 60 индексов в массиве для каждой секунды). Это всегда так.
Затем я хочу "воспроизвести" этот массив с обычной скоростью, как будто вы воспроизводите видео. Но вместо того, чтобы смотреть видео, я хочу показать текущие данные по каждому индексу, на котором вы находитесь.
Структура массива еще не создана, но я предполагаю, что просто сохраню и загрузлю файл JSON где-нибудь в базе данных. Это должно быть воспроизведено в моем интерфейсе. Я использовал Angular (6) для его построения, поэтому было бы неплохо, если бы его можно было смешать с Angular (чтобы отслеживать ход выполнения текущего индекса и привязывать значения текущего индекса к внешнему интерфейсу),
Было бы легко просто использовать 0-1-2-etc для indexx, или, может быть, что-то вроде меток времени?
Любые предложения приветствуются. Самое главное, чтобы он был быстрым, чтобы вам не понадобилась сильная установка для игры.
Заранее спасибо!
2 ответа
Вам нужно использовать setInterval
функция, в которой вы будете перебирать массив в соответствии с вашей частотой. Так что если ваша частота 60hz
это означает, что вы хотите переходить к следующему элементу в массиве после каждого 1000 / 60
миллисекунды
var data = [1, 2, 3, 4, 5]
var currentIndex = 0;
var interval = 1000 / 60
var id = setInterval(function() {
// do your thing
if(currentIndex == (data.length-1)) {
clearInterval(id)
} else {
currentIndex++
}
}, interval)
Это не особо перебирающий массив, а скорее выполнение некоторого действия после интервала времени, а затем переход к следующему элементу, и когда вы закончите с массивом, это очистит интервал. Возможно, связанный список будет более полезным, чем массив здесь
Вы можете сделать себя простым бегуном для таких вещей. Это в основном игровой движок, и вам нужен игровой цикл:)
Вот наивный пример. Пропускаем большую часть проверки и проверки ошибок здесь для пивоварения.
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();
}
}
Теперь вот пример использования. Вы можете скопировать код (как класс выше, так и блок кода ниже) и вставить его в консоль прямо здесь, на Stackru, чтобы увидеть его в работе. Вы, вероятно, хотите делать другие вещи, но вы получите суть.
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);