svga-web
Version:
A SVGA player for modern Web.
100 lines (86 loc) • 2.51 kB
text/typescript
import { noop } from './noop'
const WORKER = `onmessage = function () {
setTimeout(function() {postMessage(null)}, 1 / 60)
}`
export enum FILL_MODE {
FORWARDS = 'forwards',
BACKWARDS = 'backwards',
}
export default class Animator {
public noExecutionDelay = false
public startValue = 0
public endValue = 0
public duration = 0
public repeatNumber = 1
public loop = false
public fillRule: FILL_MODE = FILL_MODE.FORWARDS
public onUpdate: (frame: number) => unknown = noop
public onEnd: () => unknown = noop
private isRunning = false
private startTimestamp = 0
private timeoutWorker: Worker | null = null
/**
* Get current time in milliseconds
* @private
*/
private static currentTimeMillisecond(): number {
return performance ? performance.now() : new Date().getTime()
}
/**
* Start animation
* @param initialFrame
*/
public start(initialFrame: number): void {
this.isRunning = true
this.startTimestamp = Animator.currentTimeMillisecond()
if (initialFrame) {
this.startTimestamp -=
(initialFrame / (this.endValue - this.startValue)) * this.duration
}
if (this.noExecutionDelay && this.timeoutWorker === null) {
this.timeoutWorker = new Worker(
window.URL.createObjectURL(new Blob([WORKER])),
)
}
this.doFrame()
}
/**
* Stop animation
*/
public stop(): void {
this.isRunning = false
if (this.timeoutWorker !== null) {
this.timeoutWorker.terminate()
this.timeoutWorker = null
}
}
/**
* Process a frame, and process later frames repeatedly
*/
private readonly doFrame = () => {
const deltaTime = Animator.currentTimeMillisecond() - this.startTimestamp
let fraction: number
if (!this.loop && deltaTime >= this.duration * this.repeatNumber) {
fraction = this.fillRule === FILL_MODE.BACKWARDS ? 0.0 : 1.0
this.isRunning = false
} else {
fraction = (deltaTime % this.duration) / this.duration
}
const frame = (this.endValue - this.startValue) * fraction + this.startValue
this.onUpdate(frame)
if (this.isRunning) {
if (this.timeoutWorker) {
this.timeoutWorker.onmessage = this.doFrame
this.timeoutWorker.postMessage(null)
} else {
window.requestAnimationFrame(this.doFrame)
}
} else {
if (this.timeoutWorker !== null) {
this.timeoutWorker.terminate()
this.timeoutWorker = null
}
this.onEnd()
}
}
}