UNPKG

stage-js

Version:

2D HTML5 Rendering and Layout

273 lines (229 loc) 6.21 kB
import { Vec2Value } from "../common/matrix"; import { uid } from "../common/uid"; import { Easing, EasingFunction, EasingFunctionName } from "./easing"; import { Component } from "./component"; import { Pinned } from "./pin"; export type TransitionOptions = { duration?: number; delay?: number; append?: boolean; }; export type TransitionEndListener = (this: Component) => void; export class Transition implements Pinned { /** @internal */ uid = "transition:" + uid(); /** @internal */ _end: object; /** @internal */ _start: object; /** @internal */ _ending: TransitionEndListener[] = []; /** @internal */ _duration: number; /** @internal */ _delay: number; /** @internal */ _owner: Component; /** @internal */ _time: number; /** @internal */ _easing: any; /** @internal */ _next: any; /** @internal */ _hide: boolean; /** @internal */ _remove: boolean; constructor(owner: Component, options: TransitionOptions = {}) { this._end = {}; this._duration = options.duration || 400; this._delay = options.delay || 0; this._owner = owner; this._time = 0; } /** @internal */ tick(component: Component, elapsed: number, now: number, last: number) { this._time += elapsed; if (this._time < this._delay) { return; } const time = this._time - this._delay; if (!this._start) { this._start = {}; for (const key in this._end) { this._start[key] = this._owner.pin(key); } } let p = Math.min(time / this._duration, 1); const ended = p >= 1; if (typeof this._easing == "function") { p = this._easing(p); } const q = 1 - p; for (const key in this._end) { this._owner.pin(key, this._start[key] * q + this._end[key] * p); } return ended; } /** @internal */ finish() { this._ending.forEach((callback: TransitionEndListener) => { try { callback.call(this._owner); } catch (e) { console.error(e); } }); return this._next; } tween(opts?: TransitionOptions): Transition; tween(duration?: number, delay?: number): Transition; tween(a?: object | number, b?: number) { let options: TransitionOptions; if (typeof a === "object" && a !== null) { options = a; } else { options = {}; if (typeof a === "number") { options.duration = a; if (typeof b === "number") { options.delay = b; } } } return (this._next = new Transition(this._owner, options)); } duration(duration: number) { this._duration = duration; return this; } delay(delay: number) { this._delay = delay; return this; } ease(easing: EasingFunctionName | EasingFunction) { this._easing = Easing.get(easing); return this; } done(fn: TransitionEndListener) { this._ending.push(fn); return this; } hide() { this._ending.push(function () { this.hide(); }); this._hide = true; return this; } remove() { this._ending.push(function () { this.remove(); }); this._remove = true; return this; } pin(key: string, value: any): this; pin(obj: object): this; pin(key: string): any; pin(a?, b?) { if (typeof a === "object") { for (const attr in a) { pinning(this._owner, this._end, attr, a[attr]); } } else if (typeof b !== "undefined") { pinning(this._owner, this._end, a, b); } return this; } /** * @hidden @deprecated Use .done(fn) instead. */ then(fn: TransitionEndListener) { this.done(fn); return this; } /** * @hidden @deprecated this doesn't do anything anymore, call transition on the component instead. */ clear(forward: boolean) { return this; } size(w: number, h: number) { // Pin shortcut, used by Transition and Component this.pin("width", w); this.pin("height", h); return this; } width(w: number): this; width(): number; width(w?: number) { // Pin shortcut, used by Transition and Component if (typeof w === "undefined") { return this.pin("width"); } this.pin("width", w); return this; } height(h: number): this; height(): number; height(h?: number) { // Pin shortcut, used by Transition and Component if (typeof h === "undefined") { return this.pin("height"); } this.pin("height", h); return this; } offset(value: Vec2Value): this; offset(x: number, y: number): this; offset(a: number | Vec2Value, b?: number) { // Pin shortcut, used by Transition and Component if (typeof a === "object") { b = a.y; a = a.x; } this.pin("offsetX", a); this.pin("offsetY", b); return this; } rotate(a: number) { // Pin shortcut, used by Transition and Component this.pin("rotation", a); return this; } skew(value: Vec2Value): this; skew(x: number, y: number): this; skew(a: number | Vec2Value, b?: number) { // Pin shortcut, used by Transition and Component if (typeof a === "object") { b = a.y; a = a.x; } else if (typeof b === "undefined") { b = a; } this.pin("skewX", a); this.pin("skewY", b); return this; } scale(value: Vec2Value): this; scale(x: number, y: number): this; scale(s: number): this; scale(a: number | Vec2Value, b?: number) { // Pin shortcut, used by Transition and Component if (typeof a === "object") { b = a.y; a = a.x; } else if (typeof b === "undefined") { b = a; } this.pin("scaleX", a); this.pin("scaleY", b); return this; } alpha(a: number, ta?: number) { // Pin shortcut, used by Transition and Component this.pin("alpha", a); if (typeof ta !== "undefined") { this.pin("textureAlpha", ta); } return this; } } /** @internal */ function pinning(component: Component, map: object, key: string, value: number) { if (typeof component.pin(key) === "number") { map[key] = value; } else if (typeof component.pin(key + "X") === "number" && typeof component.pin(key + "Y") === "number") { map[key + "X"] = value; map[key + "Y"] = value; } }