Таймер, работающий синхронно с фокусом
Описание
Создал простой "движок" со статическим FPS-ом.
class StaticEngine extends EventTarget {
constructor() {
super();
let previous = 0;
/**
* @param {DOMHighResTimeStamp} current
* @returns {void}
*/
const callback = (current) => {
const difference = current - previous;
for (let index = 0; index < difference / this.#gap; index++) {
if (this.#focus && this.launched) this.dispatchEvent(new Event(`update`));
previous = current;
}
requestAnimationFrame(callback);
};
requestAnimationFrame(callback);
window.addEventListener(`focus`, (event) => this.#focus = true);
window.addEventListener(`blur`, (event) => this.#focus = false);
}
/** @type {boolean} */
launched = false;
/** @type {number} */
#gap = 1000 / 120;
/** @type {boolean} */
#focus = document.hasFocus();
}
У него одна большая проблема: если фокус со вкладки теряется, то вызов dispatchEvent
не происходит, но счетчик от requestAnimationFrame
накапливается и когда фокус возвращается движку придется обрабатывать тысячи циклов сразу.
И даже тот факт, что я создал логику проверки фокуса не обеспечивает надежность на 100%, так как, к примеру в Edge есть баг при открытии Inspect element
когда обработчик blur
не срабатывает, или же когда переключаемся между вкладками через Alt + Tab.
К тому же я хочу получить возможность использовать движок в Worker-ах, а там document
или window
недоступны. Я, конечно, могу состояние фокуса отправить через message
, но это нарушит целостность класса и костыль скорее чем решение.
Вопрос
Основной вопрос: как мне остановить таймер, когда вкладка не в фокусе?
Если основной не удается решить: чем мне заменить логику фокуса, чтобы хотя бы нормально работала в Worker-ах?
Ответы (1 шт):
Решение всему - setTimeout
Заменив requestAnimationFrame
на setTimeout
мы уберем зависимость от фокуса, и зависимость от document
. Движок будет автоматически оптимизироваться для работы в фоновом режиме. А так же будет без проблем работать в Worker-ах.
class StaticEngine extends EventTarget {
constructor() {
super();
let previous = 0;
/**
* @returns {void}
*/
const callback = () => {
const current = performance.now();
const difference = current - previous;
for (let index = 0; index < difference / this.#gap; index++) {
if (this.launched) this.dispatchEvent(new Event(`update`));
previous = current;
}
setTimeout(callback);
};
setTimeout(callback);
}
/** @type {boolean} */
launched = false;
/** @type {number} */
#gap = 1000 / 120;
}