From 46f3a07c856576e3aa194b7b89c2f8254fc8a5de Mon Sep 17 00:00:00 2001 From: Victor Westerlund Date: Sun, 26 Dec 2021 14:07:37 +0100 Subject: [PATCH] Replace `setTimeout` with `requestAnimationFrame` (#3) * dev21w45-a * dev21w45-b * Implement requestAnimationFrame This commit replaces setTimeout with a working example of requestAnimationFrame. Some further testing will have to be done * Add support for loop --- Monkeydo.mjs | 6 +--- monkey/Monkey.js | 71 +++++++++++++++++++++++------------------ monkey/MonkeyMaster.mjs | 7 ++-- 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/Monkeydo.mjs b/Monkeydo.mjs index 918319a..1255552 100644 --- a/Monkeydo.mjs +++ b/Monkeydo.mjs @@ -37,11 +37,7 @@ export default class Monkeydo extends MonkeyMaster { async play(manifest = null) { if(!this.ready && !manifest) throw new Error("Can not start playback without a manifest"); - if(manifest) { - const load = this.load(manifest) - load.then(() => this.start()); - return; - } + if(manifest) await this.load(manifest); return await this.start(); } } \ No newline at end of file diff --git a/monkey/Monkey.js b/monkey/Monkey.js index 8d0bd09..d52dcbe 100644 --- a/monkey/Monkey.js +++ b/monkey/Monkey.js @@ -5,44 +5,55 @@ importScripts("https://unpkg.com/comlink/dist/umd/comlink.js"); class Monkey { constructor() { this.flags = new Uint8ClampedArray(3); - this.tasks = []; - this.tasksLength = 0; - this.i = 0; - // Runtime task queue - this.queue = { - thisTask: null, - nextTask: null + + this.tasks = { + tasks: [], + length: 0, + _target: 0, + _i: 0, + set manifest(manifest) { + this.tasks = manifest; + this.length = this.tasks.length - 1; + }, + get task() { + return this.tasks[this._i]; + }, + get target() { + return this._target; + } } } - // Task scheduler + // Advance to the next task or loop next() { - if(this.flags[0] === 0 || this.flags[2] === 0) return this.abort(); - const task = this.tasks[this.i]; - - // Run task after delay - this.queue.thisTask = setTimeout(() => { - // Dispatch task to main thread - postMessage(["TASK",task]); - this.i++; - },task[0]); - - // Loop until flag is 0 or infinite if 255 - if(this.i === this.tasksLength) { - this.i = -1; - if(this.flags[1] < 255) this.flags[2]--; + // Reset index and loop if out of tasks + if(this.tasks._i >= this.tasks.length) { + this.tasks._i = -1; + if(this.flags[1] === 255) return; // Loop forever + this.flags[2] -= 1; } + + this.tasks._i++; + const nextTask = this.tasks.task; + this.tasks._target = performance.now() + nextTask[0]; + } - // Queue the next task - this.queue.nextTask = setTimeout(() => this.next(),task[0]); + // Main event loop, runs on every frame + tick() { + if(this === undefined) return; + if(this.flags[0] === 0 || this.flags[2] === 0) return this.abort(); + + const frame = Math.min(performance.now(),this.tasks.target); + if(frame == this.tasks.target) { + postMessage(["TASK",this.tasks.task]); + this.next(); + } + + requestAnimationFrame(this.tick.bind(this)); } abort() { this.flags[2] = 0; // Playing: false - clearTimeout(this.queue.thisTask); - clearTimeout(this.queue.nextTask); - this.queue.thisTask = null; - this.queue.nextTask = null; } // Set or get a runtime flag @@ -73,9 +84,7 @@ class Monkey { reject("Failed to load manifest"); } } - this.tasks = manifest.tasks; - // Store length as property so we don't have to calculate the offset each iteration of next() - this.tasksLength = manifest.tasks.length - 1; + this.tasks.manifest = manifest.tasks; this.flags[0] = 1; // Manifest loaded: true resolve(); }); diff --git a/monkey/MonkeyMaster.mjs b/monkey/MonkeyMaster.mjs index da3ee63..fd7c35d 100644 --- a/monkey/MonkeyMaster.mjs +++ b/monkey/MonkeyMaster.mjs @@ -47,11 +47,12 @@ export default class MonkeyMaster { const Monkey = Comlink.wrap(worker); this.comlink = await new Monkey(); - // Wait for comlink to initialize proxy and send queued flags + // Wait for comlink to spin up return await new Promise((resolve,reject) => { - if(!this.comlink) reject("Failed to open proxy to worker"); + if(!this.comlink) reject("Failed to establish Comlink with worker"); this.ready = true; + // Send queued flags when worker is ready this.queue.sendAllFlags(); resolve(); }); @@ -132,6 +133,6 @@ export default class MonkeyMaster { if(playing > 0) return; await this.setFlag("playing",loop); - return await this.comlink.next(); + return await this.comlink.tick(); } } \ No newline at end of file