UNPKG

@dill-pixel/plugin-rive

Version:

Rive

394 lines (393 loc) 14.1 kB
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} (@rive-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