@visactor/vrender-animate
Version:
This module provides a graph-based animation system for VRender.
134 lines (130 loc) • 5.16 kB
JavaScript
import { EventEmitter } from "@visactor/vutils";
import { application, PerformanceRAF, STATUS } from "@visactor/vrender-core";
const performanceRAF = new PerformanceRAF;
class RAFTickHandler {
constructor() {
this.released = !1;
}
tick(interval, cb) {
performanceRAF.addAnimationFrameCb((() => {
if (!this.released) return cb(this);
}));
}
release() {
this.released = !0;
}
getTime() {
return Date.now();
}
}
export class DefaultTicker extends EventEmitter {
constructor(stage) {
super(), this.timelines = [], this.frameTimeHistory = [], this.handleTick = (handler, params) => {
const {once: once = !1} = null != params ? params : {};
if (this.ifCanStop()) return this.stop(), !1;
const currentTime = handler.getTime();
this._lastTickTime = currentTime, this.lastFrameTime < 0 && (this.lastFrameTime = currentTime - this.interval + this.timeOffset,
this.frameTimeHistory.push(this.lastFrameTime));
const delta = currentTime - this.lastFrameTime, skip = this.checkSkip(delta);
return skip || (this._handlerTick(delta), this.lastFrameTime = currentTime, this.frameTimeHistory.push(this.lastFrameTime)),
once || handler.tick(this.interval, this.handleTick), !skip;
}, this._handlerTick = delta => {
this.status === STATUS.RUNNING && (this.tickCounts++, this.timelines.forEach((timeline => {
timeline.tick(delta);
})), this.emit("tick", delta));
}, this.init(), this.lastFrameTime = -1, this.tickCounts = 0, this.stage = stage,
this.autoStop = !0, this.interval = 16, this.computeTimeOffsetAndJitter();
}
bindStage(stage) {
this.stage = stage;
}
computeTimeOffsetAndJitter() {
this.timeOffset = Math.floor(Math.random() * this.interval), this._jitter = Math.min(Math.max(.2 * this.interval, 6), .7 * this.interval);
}
init() {
this.interval = 16, this.status = STATUS.INITIAL, application.global.hooks.onSetEnv.tap("graph-ticker", (() => {
this.initHandler(!1);
})), application.global.env && this.initHandler(!1);
}
addTimeline(timeline) {
this.timelines.push(timeline);
}
remTimeline(timeline) {
this.timelines = this.timelines.filter((t => t !== timeline));
}
getTimelines() {
return this.timelines;
}
initHandler(force = !1) {
this.setupTickHandler(force);
}
setupTickHandler(force = !1) {
if (!force && this.tickerHandler) return !0;
const handler = new RAFTickHandler;
return this.tickerHandler && this.tickerHandler.release(), this.tickerHandler = handler,
!0;
}
setInterval(interval) {
this.interval = interval, this.computeTimeOffsetAndJitter();
}
getInterval() {
return this.interval;
}
setFPS(fps) {
this.setInterval(Math.floor(1e3 / fps));
}
getFPS() {
return 1e3 / this.interval;
}
tick(interval) {
this.tickerHandler.tick(interval, (handler => this.handleTick(handler, {
once: !0
})));
}
tickTo(t) {
this.tickerHandler.tickTo && this.tickerHandler.tickTo(t, (handler => {
this.handleTick(handler, {
once: !0
});
}));
}
pause() {
return this.status !== STATUS.INITIAL && (this.status = STATUS.PAUSE, !0);
}
resume() {
return this.status !== STATUS.INITIAL && (this.status = STATUS.RUNNING, !0);
}
ifCanStop() {
if (this.autoStop) {
if (!this.timelines.length) return !0;
if (this.timelines.every((timeline => !timeline.isRunning()))) return !0;
}
return !1;
}
start(force = !1) {
if (this.status === STATUS.RUNNING) return !1;
if (!this.tickerHandler) return !1;
if (!force) {
if (this.status === STATUS.PAUSE) return !1;
if (this.ifCanStop()) return !1;
}
return this.status = STATUS.RUNNING, this.tickerHandler.tick(0, this.handleTick),
!0;
}
stop() {
this.status = STATUS.INITIAL, this.setupTickHandler(!0), this.lastFrameTime = -1;
}
trySyncTickStatus() {
this.status === STATUS.INITIAL && this.timelines.some((timeline => timeline.isRunning())) ? this.start() : this.status === STATUS.RUNNING && this.timelines.every((timeline => !timeline.isRunning())) && this.stop();
}
release() {
var _a;
this.stop(), this.timelines = [], null === (_a = this.tickerHandler) || void 0 === _a || _a.release(),
this.tickerHandler = null, this.lastFrameTime = -1;
}
checkSkip(delta) {
var _a, _b, _c;
if ("performance" === (null === (_c = null === (_b = null === (_a = this.stage) || void 0 === _a ? void 0 : _a.params) || void 0 === _b ? void 0 : _b.optimize) || void 0 === _c ? void 0 : _c.tickRenderMode)) return !1;
return delta < this.interval + 2 * (Math.random() - .5) * this._jitter;
}
}