hzengine-core
Version:
An Visual Novel Engine for Zepp OS
215 lines (189 loc) • 6.25 kB
text/typescript
import { HZEngineCore, Storage, UI } from "../../index.js";
import {
Save,
CustomSave,
} from "../../storage/decorator.js";
import { Animation, Profile, Wrapper } from "./animation.js";
// import { Time } from "@zos/sensor";
export class AnimationPlugin {
constructor(public _core: HZEngineCore) {
console.log("[AnimationPlugin] init");
_core.loadPlugin("animation", () => this)
_core.on("anime.cb", this._timerCb.bind(this));
_core.async.addRepeatTask("anime.cb", [], 0); // 此處周期應考慮加個sync update
this._lastCbUtc = this._core.platform.getTime()
}
private accessor _nextAnimationId: number = 1; // self-increment
private accessor _animationMap: Record<
string,
AnimationPlugin.AnimationItem
> = {};
// private _timeSensor = new Time();
private _lastCbUtc: number;
public applyAnimation<P extends Profile.PropsType>({
profile,
targetView,
options,
}: {
profile: Profile<P>;
targetView: UI.View<Storage.Saveable<unknown>>;
options?: AnimationPlugin.Options;
}): number {
const id = this._nextAnimationId++;
let animation = new Animation(profile, {
// @ts-ignore
initProps: targetView.prop ?? {},
wrappers: options?.customWrappers,
});
if (!targetView.id) throw "targetView.id must be not null";
this._animationMap[id] = {
destroyOnEnd: options?.destroyOnEnd ?? false,
isSave: options?.isSave ?? true,
instance: animation,
targetViewId: targetView.id!,
};
this._linkAnimationCb(animation, id);
animation.goto(0);
this._core.on("afterLoadArchive", () => {
this._lastCbUtc = this._core.platform.getTime();
});
return id;
}
public stopAnimation(id: number) {
if (this._animationMap[id] !== undefined) {
this._animationMap[id].instance.stop();
delete this._animationMap[id];
}
}
private _nextTempAnimationId: number = -1; // self-decrement
_tempAnimationMap: Record<string, Animation> = {};
public createTempAnimation<P extends Profile.PropsType = unknown>({
profile,
onFrame,
onEnd,
initProps,
wrappers,
}: {
profile: Profile<P>;
onFrame?: (props: Partial<P>) => void;
onEnd?: () => void;
initProps?: Partial<P>;
wrappers?: Record<string, Wrapper>;
}): number {
let id = this._nextTempAnimationId--;
let animation = new Animation(profile, {
initProps,
wrappers,
});
animation.onFrame = onFrame ?? null;
animation.onEnd = () => {
if (onEnd) onEnd();
delete this._tempAnimationMap[id];
};
this._tempAnimationMap[id] = animation
return id;
}
public clearTempAnimation(id: number) {
if (this._tempAnimationMap[id] !== undefined) {
this._tempAnimationMap[id].stop();
delete this._tempAnimationMap[id];
}
}
private _timerCb() {
let utc = this._core.platform.getTime();
let delta_time = (utc - this._lastCbUtc) / 1000;
this._lastCbUtc = utc;
// this._core.debug.log(`delta time: ${delta_time}`);
for (let id in this._animationMap) {
this._animationMap[id].instance.step(delta_time);
}
for (let id in this._tempAnimationMap) {
this._tempAnimationMap[id].step(delta_time);
}
}
private _frameCb(props: Record<string, any>, id: number) {
if (this._animationMap[id] !== undefined) {
let view = this._core.ui.getView(this._animationMap[id].targetViewId);
if (!view) {
this._core.debug.log(`view ${id} not found, stop animation`);
this.stopAnimation(id); // TODO
return;
}
view.commit(props);
// this._core.debug.log(`update view ${view.name} with ${JSON.stringify(props)}`);
}
}
private _linkAnimationCb(animation: Animation, id: number) {
animation.onFrame = (props) => {
// console.log("onFrame cb");
this._frameCb(props, id);
};
animation.onEnd = () => this._endCb(id);
}
private _endCb(id: number) {
if (this._animationMap[id] !== undefined) {
if (this._animationMap[id].destroyOnEnd) {
let view = this._core.ui.getView(id);
if (view) this._core.ui.destroyView(view);
}
delete this._animationMap[id];
console.log("map item", this._animationMap[id]);
}
else console.log("nf map item", this._animationMap[id]);
}
}
namespace AnimationPlugin {
export interface Options {
destroyOnEnd?: boolean;
isSave?: boolean;
customWrappers?: Record<string, Wrapper>;
}
export interface AnimationItem {
destroyOnEnd: boolean;
isSave: boolean;
targetViewId: number;
instance: Animation;
}
export namespace AnimationItem {
export interface Serialized {
destroyOnEnd: boolean;
isSave: boolean;
targetViewId: number;
serializedInstance: Animation.Serialized;
}
}
}