Skip to content

Commit

Permalink
feat(timeline): Run in the background
Browse files Browse the repository at this point in the history
By using a Web Worker we can run our rAF in another thread which can still update the main-thread.

#108
  • Loading branch information
TimPietrusky committed Aug 13, 2019
1 parent a865ded commit 763a720
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 26 deletions.
73 changes: 47 additions & 26 deletions src/components/timeline/timeline-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,9 @@ class TimelineManager extends connect(store)(LitElement) {
constructor() {
super()

this.timeoutId = undefined

this.progress = 0

document.addEventListener('keypress', e => {
const { code } = e

// Start playback when active element is the body
if (code === 'Space' && e.target === document.body) {
e.preventDefault()
this.handlePlay()
}
})
this.worker = undefined
}

static get properties() {
Expand All @@ -47,6 +37,39 @@ class TimelineManager extends connect(store)(LitElement) {
}
}

connectedCallback() {
super.connectedCallback()

// Toggle the playback when the space-key is pressed
document.addEventListener('keypress', e => {
const { code } = e

// Start playback when active element is the body
if (code === 'Space' && e.target === document.body) {
e.preventDefault()
this.handlePlay()
}
})

// Worker to handle background playback
this.worker = new Worker('components/timeline/timeline-worker.js')
// Trigger the timeline loop when we receive a message from the worker
this.worker.addEventListener('message', () => {
this.loop()
})
// Update the playback state when the timeline-manager is initially loaded
this.observePlaying()
}

disconnectedCallback() {
super.disconnectedCallback()

document.removeEventListener('keypress')

this.worker.removeEventListener('message')
this.worker.terminate()
}

// Start / stop the playback loop whenever isPlaying is changed
set playing(value) {
// Only update the value if it's actually different
Expand Down Expand Up @@ -93,14 +116,18 @@ class TimelineManager extends connect(store)(LitElement) {
// @TODO: Save the current progress into state
// store.dispatch(setTimelineProgress(this.progress))
observePlaying() {
if (this.isPlaying) {
console.log('playing')

this.loop()
} else {
console.log('stopped')
// Based on isPlaying we have to start or stop the playback-loop in the worker
this.updateWorker(this.isPlaying)
}

clearTimeout(this.timeoutId)
/**
* Start or stop the playback-loop of the worker
*
* @param {boolean} isPlaying - Is the timeline running or not?
*/
updateWorker(isPlaying) {
if (this.worker !== undefined) {
this.worker.postMessage({ isPlaying })
}
}

Expand All @@ -112,10 +139,10 @@ class TimelineManager extends connect(store)(LitElement) {
if (this.isPlaying) {
const now = new Date()
this.progress = now.getTime()

// Start all scenes that are not started yet
for (let i = 0; i < this.timelineScenes.length; i++) {
const scene = this.timelineScenes[i];
const scene = this.timelineScenes[i]

// Start the scene if it wasn't started yet
if (scene.started === undefined) {
Expand Down Expand Up @@ -171,12 +198,6 @@ class TimelineManager extends connect(store)(LitElement) {

// Send the universe to the FivetwelveManager
window.dispatchEvent(new CustomEvent('send-universe-to-fivetwelve', { detail: { now } }))

this.timeoutId = setTimeout(() => {
// Get the next frame
requestAnimationFrame(this.loop.bind(this))
}, 1000 / 30)

}
}

Expand Down
39 changes: 39 additions & 0 deletions src/components/timeline/timeline-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
let isWorkerPlaying = false
let timeoutId = null

// playback-loop
const loop = () => {

// Only trigger the next iteration if the loop is actually started
if (isWorkerPlaying) {
// Inform the main-thread that we are playing
postMessage(isWorkerPlaying)

timeoutId = setTimeout(() => {

// Trigger the next iteration of the playback-loop
requestAnimationFrame(loop)

// Limit the loop to max 60 fps
}, 1000 / 60)
}

}

// The main-thread might start or stop the playback-loop
self.addEventListener('message', e => {
const { isPlaying } = e.data

// Make sure that the loop can't be started or stopped more than once
if (isPlaying === isWorkerPlaying) {
return
}

isWorkerPlaying = isPlaying

if (isWorkerPlaying) {
loop()
} else {
clearTimeout(timeoutId)
}
})

0 comments on commit 763a720

Please sign in to comment.