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
This commit is contained in:
Victor Westerlund 2021-12-26 14:07:37 +01:00 committed by GitHub
parent a6e51c1e48
commit 46f3a07c85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 39 deletions

View file

@ -37,11 +37,7 @@ export default class Monkeydo extends MonkeyMaster {
async play(manifest = null) { async play(manifest = null) {
if(!this.ready && !manifest) throw new Error("Can not start playback without a manifest"); if(!this.ready && !manifest) throw new Error("Can not start playback without a manifest");
if(manifest) { if(manifest) await this.load(manifest);
const load = this.load(manifest)
load.then(() => this.start());
return;
}
return await this.start(); return await this.start();
} }
} }

View file

@ -5,44 +5,55 @@ importScripts("https://unpkg.com/comlink/dist/umd/comlink.js");
class Monkey { class Monkey {
constructor() { constructor() {
this.flags = new Uint8ClampedArray(3); this.flags = new Uint8ClampedArray(3);
this.tasks = [];
this.tasksLength = 0; this.tasks = {
this.i = 0; tasks: [],
// Runtime task queue length: 0,
this.queue = { _target: 0,
thisTask: null, _i: 0,
nextTask: null 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() { next() {
if(this.flags[0] === 0 || this.flags[2] === 0) return this.abort(); // Reset index and loop if out of tasks
const task = this.tasks[this.i]; if(this.tasks._i >= this.tasks.length) {
this.tasks._i = -1;
// Run task after delay if(this.flags[1] === 255) return; // Loop forever
this.queue.thisTask = setTimeout(() => { this.flags[2] -= 1;
// 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]--;
} }
// Queue the next task this.tasks._i++;
this.queue.nextTask = setTimeout(() => this.next(),task[0]); const nextTask = this.tasks.task;
this.tasks._target = performance.now() + nextTask[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() { abort() {
this.flags[2] = 0; // Playing: false 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 // Set or get a runtime flag
@ -73,9 +84,7 @@ class Monkey {
reject("Failed to load manifest"); reject("Failed to load manifest");
} }
} }
this.tasks = manifest.tasks; this.tasks.manifest = 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.flags[0] = 1; // Manifest loaded: true this.flags[0] = 1; // Manifest loaded: true
resolve(); resolve();
}); });

View file

@ -47,11 +47,12 @@ export default class MonkeyMaster {
const Monkey = Comlink.wrap(worker); const Monkey = Comlink.wrap(worker);
this.comlink = await new Monkey(); 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) => { 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; this.ready = true;
// Send queued flags when worker is ready
this.queue.sendAllFlags(); this.queue.sendAllFlags();
resolve(); resolve();
}); });
@ -132,6 +133,6 @@ export default class MonkeyMaster {
if(playing > 0) return; if(playing > 0) return;
await this.setFlag("playing",loop); await this.setFlag("playing",loop);
return await this.comlink.next(); return await this.comlink.tick();
} }
} }