@dill-pixel/plugin-rive
Version:
Rive
394 lines (393 loc) • 14.1 kB
JavaScript
import g from "@rive-app/canvas-advanced-lite";
import { Plugin as _, WithSignals as y, Container as b, Signal as r, destroyCanvas as x, Logger as f } from "dill-pixel";
import { extensions as w, LoaderParserPriority as M, ExtensionType as P, BrowserAdapter as A, checkExtension as S, Texture as I, Assets as C } from "pixi.js";
const E = "6.2.3", R = "2.30.1", v = {
wasmPath: "https://unpkg.com/@rive-app/canvas-advanced-lite@2.26.1/rive.wasm"
};
class o extends _ {
constructor() {
super(...arguments), this.id = "rive", this._options = v, this._addedExtensions = !1;
}
hello() {
const t = `%c Dill Pixel Rive Plugin v${E} | %cRive v${R} (-app/canvas-advanced-lite)`;
console.log(
t,
"background: rgba(31, 41, 55, 1);color: #74b64c",
"background: rgba(31, 41, 55, 1);color: #e91e63"
);
}
async initialize(t) {
this._options = { ...v, ...t }, this.hello(), o.ID = this.id, this._addLoaderExtensions(), this.rive || (this.rive = await g({ locateFile: () => this._options.wasmPath }));
}
cleanup() {
}
destroy() {
this.cleanup(), super.destroy();
}
_addLoaderExtensions() {
this._addedExtensions || (w.add({
name: "loadRive",
extension: {
type: P.LoadParser,
priority: M.High
},
test(t) {
return S(t, ".riv");
},
async load(t) {
const i = await A.fetch(t);
return new Uint8Array(await i.arrayBuffer());
}
}), this._addedExtensions = !0);
}
}
class z extends y(b) {
/**
* Constructor will load Rive wasm if it not loaded yet
* and create instances of Rive scene components (artboard, animation, stateMachine)
* after initialize will call onReady method and run animation if autoPlay was setted
* @param {RiveOptions} options initial component options
*/
constructor(t) {
super({ autoResize: !1, autoUpdate: !1, priority: -1 }), this.options = t, this.onStateChange = new r(), this.onReady = new r(), this.onPlay = new r(), this.onStop = new r(), this.onPause = new r(), this.onResume = new r(), this.animations = [], this.stateMachines = [], this.inputFields = /* @__PURE__ */ new Map(), this._debug = !1, this._enabled = !1, this._align = "center", this._fit = "contain", this._maxWidth = 0, this._maxHeight = 0, this._debug = t.debug ?? !1, this.initEvents(t.interactive ?? !1), t.autoInit !== !1 && this.initialize();
}
get align() {
return this._align;
}
set align(t) {
this._align = t;
}
get fit() {
return this._fit;
}
set fit(t) {
this._fit = t, this.updateSize();
}
get maxWidth() {
return this._maxWidth;
}
set maxWidth(t) {
this._maxWidth = t, this.updateSize();
}
get maxHeight() {
return this._maxHeight;
}
set maxHeight(t) {
this._maxHeight = t, this.updateSize();
}
get rive() {
return this.plugin.rive;
}
get view() {
return this._view;
}
get plugin() {
return this.app.getPlugin(o.ID);
}
async initialize() {
await this.initRive(this.options.asset).then(() => {
try {
this.loadArtboard(this.options.artboard), this.loadStateMachine(this.options.stateMachine), this.playAnimation(this.options.animation), this.options.fit && (this.fit = this.options.fit), this.options.align && (this.align = this.options.align), this.options.maxWidth && (this.maxWidth = this.options.maxWidth), this.options.maxHeight && (this.maxHeight = this.options.maxHeight), (this.options.maxHeight !== void 0 || this.options.maxWidth !== void 0) && this.updateSize(), this.options.autoPlay ? this.enable() : this.update(), this.onReady.emit(this.rive);
} catch (t) {
console.error(t);
}
});
}
/**
* Enable rive scene animation
*/
enable() {
this._enabled = !0, this.rive && this.app.ticker.add(this.update);
}
/**
* Disable rive scene animation
*/
disable() {
this._enabled = !1, this.rive && this.app.ticker.remove(this.update);
}
/**
* Load Rive scene artboard by name or load default artboard if name is not set
* Rive should be initialized before (RiveOptions.onReady was emited)
* @param {string|number} artboard name of the loading artboard
*/
loadArtboard(t) {
var i, e;
if (this.artboard)
return this._destroyInternals(), this.options.artboard = t, this.initialize();
this._file && this._canvas && (this.artboard = t ? this._file.artboardByName(t) : this._file.defaultArtboard(), this._view ? this._view.texture.source.update() : this._view = this.add.sprite({
asset: I.from(this._canvas, !1),
anchor: ((i = this.options) == null ? void 0 : i.anchor) ?? 0,
scale: ((e = this.options) == null ? void 0 : e.scale) ?? 1,
layout: !1
})), this.updateSize();
}
/**
* Load Rive state machines by names or load first state machine
* Artbaord should be loaded before
* Will load first state machine if name is empty
* @param {string|number} machines name or names of the loading state machines
*/
loadStateMachine(t = []) {
if (!(!this.artboard || !this.rive)) {
if (typeof t == "string") t = [t];
else if (!t.length) {
const i = this.artboard.stateMachineByIndex(0);
t = i ? [i.name] : [];
}
t.map((i) => {
const e = this.artboard.stateMachineByName(i);
this.unloadStateMachine(i), this.stateMachines.push(new this.rive.StateMachineInstance(e, this.artboard));
}), this.initInputFields();
}
}
/**
* Unload state machine and destroy instance
* @param {string} name name of the state machine
*/
unloadStateMachine(t) {
this.stateMachines = this.stateMachines.filter((i) => i.name === t ? (i.delete(), !1) : !0);
}
/**
* Play Rive animation by name (artbaord should be loaded before)
* You can play only one timeline animation at the same time.
* If animation is looped or it's a pingpong, it will be repeated endlessly
* otherwise it plays only once
*
* TODO: add onStart/onEnd/onLoop methods for animation
*
* @param {string|number} animations animation name or array of nmaes
*/
playAnimation(t = []) {
if (!(!this.artboard && !this.rive)) {
if (typeof t == "string") t = [t];
else if (!t.length && !this.stateMachines.length) {
const i = this.artboard.animationByIndex(0);
t = i ? [i.name] : [];
}
t.map((i) => {
const e = this.artboard.animationByName(i);
this.stopAnimation(i);
const s = new this.rive.LinearAnimationInstance(e, this.artboard);
this.animations.push(s);
}), this.onPlay.emit(t);
}
}
/**
* Stop current animation and destroy Rive animation instance
*/
stopAnimation(t) {
const i = Array.isArray(t) ? t : [t];
this.animations = this.animations.filter((e) => i.includes(e.name) ? (e.delete(), !1) : !0), this.onStop.emit(t);
}
pause() {
this._paused = !0, this.onPause.emit();
}
resume() {
this._paused = !1, this.onResume.emit();
}
/**
* Get list of available artboards in current Rive file
*/
getAvailableArtboards() {
const t = [];
if (this._file)
for (let i = 0; i < this._file.artboardCount(); i++)
t[i] = this._file.artboardByIndex(i).name;
return t;
}
/**
* Get list of available state machines in current artboard
*/
getAvailableStateMachines() {
const t = [];
if (this.artboard)
for (let i = 0; i < this.artboard.stateMachineCount(); i++)
t[i] = this.artboard.stateMachineByIndex(i).name;
return t;
}
/**
* Get list of available animations in current artboard
*/
getAvailableAnimations() {
const t = [];
if (this.artboard)
for (let i = 0; i < this.artboard.animationCount(); i++)
t[i] = this.artboard.animationByIndex(i).name;
return t;
}
/**
* Recalculate and update sizes of ofscreencanvas due to artboard size
* Artboard should be loaded before
*/
updateSize() {
var t;
if (this.artboard && this.rive && this._renderer && this._canvas) {
const i = this.artboard.bounds, { minX: e, minY: s, maxX: a, maxY: n } = i, h = a - e, m = n - s, d = this._maxWidth || h, l = this._maxHeight || m, c = this.rive.Fit[this._fit], u = this.rive.Alignment[this._align], p = { minX: 0, minY: 0, maxX: d, maxY: l };
this._canvas.width = d, this._canvas.height = l, this._aligned = (t = this.rive) == null ? void 0 : t.computeAlignment(c, u, p, i), this._renderer.align(c, u, p, i), this._view.texture.source.update();
}
}
/**
* Receive input fields from all active state machines
*/
initInputFields() {
const { bool: t, trigger: i } = this.rive.SMIInput;
this.inputFields.clear(), this.stateMachines.forEach((e) => {
for (let s = 0; s < e.inputCount(); s++) {
let a;
const n = e.input(s);
n.type == t ? a = n.asBool() : n.type == i ? a = n.asTrigger() : a = n.asNumber(), this.inputFields.set(n.name, a);
}
});
}
/**
* Get state machine input field by name
* @param {string} name input field name
* @returns {number|boolean} value of the input field
*/
getInputValue(t) {
const i = this.inputFields.get(t);
return i && i.value;
}
/**
* Set state machine input field value by name
* @param {string} name of the input field
* @param {number|boolean} value of the input field
*/
setInput(t, i) {
var s;
const e = this.inputFields.get(t);
e && e.type !== ((s = this.rive) == null ? void 0 : s.SMIInput.trigger) && (e.value = i);
}
/**
* Trigger state machine input field
* @param {string} name of the trigger field
*/
fireTrigger(t) {
var e;
const i = this.inputFields.get(t);
i && i.type === ((e = this.rive) == null ? void 0 : e.SMIInput.trigger) && i.fire();
}
/**
* Destroy all component resources
*/
destroy() {
var t, i;
this._destroyInternals();
try {
(t = this._renderer) == null || t.delete();
} catch {
}
try {
(i = this._file) == null || i.delete();
} catch {
}
super.destroy();
}
update(t = this.app.ticker) {
if (this._paused || !this.artboard || !this._renderer || !this.rive)
return;
const i = t.elapsedMS / 1e3;
this.advanceStateMachines(i), this.advanceAnimations(i), this.artboard.advance(i), this._renderer.clear(), this._renderer.save(), this.artboard.draw(this._renderer), this._renderer.restore(), this._renderer.flush(), this.view.texture.source.update(), this.rive.resolveAnimationFrame();
}
handlePointerdown(t) {
const i = this.translatePoint(t.global);
this.stateMachines.map((e) => e.pointerDown(...i));
}
handlePointerup(t) {
const i = this.translatePoint(t.global);
this.stateMachines.map((e) => e.pointerUp(...i));
}
handlePointermove(t) {
const i = this.translatePoint(t.global);
this.stateMachines.map((e) => e.pointerMove(...i));
}
_destroyInternals() {
var t;
this.disable(), this.off("pointerdown", this.handlePointerdown), this.off("pointerup", this.handlePointerup), this.off("pointermove", this.handlePointermove);
try {
this.stateMachines.map((i) => i.delete());
} catch {
}
try {
this.animations.map((i) => i.delete());
} catch {
}
try {
(t = this.artboard) == null || t.delete();
} catch {
}
this.animations = [], this.stateMachines = [], this.view.texture.destroy(!0), this.view.destroy(), this.artboard = null, this._view = null, this._canvas && x(this._canvas), this._canvas = null;
}
/**
* Will load wasm and rive sprite asset from assets library
* also create offscreen canvas and rive renderer
* @param riv
*/
async initRive(t) {
const i = typeof t == "string" ? await C.load(t) : t;
this._file = await this.rive.load(i), this._canvas = this.createCanvas(), this._renderer = this.rive.makeRenderer(this._canvas, !0);
}
/**
* Attach pointer events to the pixi sprite and pass them to the Rive state machine
* @param {boolean} interactive true if we need to attach pointer events to sprite
*/
initEvents(t) {
t && (this.options.cursor && (this.cursor = this.options.cursor), this.eventMode = "static", this.on("pointerdown", this.handlePointerdown), this.on("onpointerup", this.handlePointerup), this.on("onpointermove", this.handlePointermove));
}
/**
* Convert global Pixi.js coordinates to Rive point coordinates
* @param {{x:number,y:number}} global point coordinates
* @returns
*/
translatePoint(t) {
const { x: i, y: e } = this.toLocal(t), { tx: s, ty: a, xx: n, yy: h } = this._aligned || { tx: 0, ty: 0, xx: 1, yy: 1 };
return [(i - s) / n, (e - a) / h];
}
/**
* Will create offscreen canvas
* In debug mode will create a regular canvas and display it over the page
*/
createCanvas() {
if (this._debug) {
const t = document.createElement("canvas");
return t.style.position = "absolute", t.style.right = "0", t.style.top = "0", t.style.width = "auto", t.style.height = "auto", document.body.appendChild(t), t;
}
return new OffscreenCanvas(100, 100);
}
/**
* Play all state machines animations
* @param {number} elapsed time from last update
*/
advanceStateMachines(t) {
this.stateMachines.map((i) => {
const e = i.reportedEventCount();
if (e) {
f.log(`state machine ${i.name} reported ${e} events`);
for (let s = 0; s < e; s++) {
const a = i.reportedEventAt(s);
a && f.log(`event ${a.name} fired`);
}
}
if (i.advance(t), i.stateChangedCount()) {
const s = [];
for (let a = 0; a < i.stateChangedCount(); a++)
s.push(i.stateChangedNameByIndex(a));
s.length && this.onStateChange.emit(s);
}
});
}
/**
* Play all scene animations
* @param {number} elapsed time from last update
*/
advanceAnimations(t) {
this.animations.map((i) => {
i.advance(t), i.apply(1);
});
}
}
export {
z as RiveEntity,
o as default
};
//# sourceMappingURL=dill-pixel-plugin-rive.mjs.map