@pixodesk/svg-animator-vue 1.0.19 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -3
- package/dist/index.cjs +64 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -9
- package/dist/index.d.ts +3 -9
- package/dist/index.js +64 -26
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +304 -91
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ const animator = ref<VueAnimatorApi | null>(null);
|
|
|
62
62
|
</template>
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
`VueAnimatorApi` methods: `play()`, `pause()`, `cancel()`, `finish()`, `isPlaying()`, `getCurrentTime()`, `setCurrentTime(ms)`.
|
|
65
|
+
`VueAnimatorApi` methods: `play()`, `pause()`, `cancel()`, `finish()`, `isPlaying()`, `setPlaybackRate(rate)`, `getCurrentTime()`, `setCurrentTime(ms)`.
|
|
66
66
|
|
|
67
67
|
### Controlled time
|
|
68
68
|
|
|
@@ -83,7 +83,7 @@ Render a single frame at a specific point in time:
|
|
|
83
83
|
| `autoplay` | `boolean` | Use triggers from the document |
|
|
84
84
|
| `play` | `boolean` | Start playback, ignoring document triggers |
|
|
85
85
|
| `pause` | `boolean` | Pause current playback |
|
|
86
|
-
| `time` | `number` | Seek to a
|
|
86
|
+
| `time` | `number` | Seek to a fraction (0–1) of the whole timeline (duration × iterations) |
|
|
87
87
|
| `timeMs` | `number` | Seek to a time in milliseconds |
|
|
88
88
|
| `mode` | `'auto' \| 'webapi' \| 'frames'` | Animation engine |
|
|
89
89
|
| `duration` | `number` | Duration override (ms) |
|
|
@@ -95,6 +95,10 @@ Render a single frame at a specific point in time:
|
|
|
95
95
|
| `startOn` | `'load' \| 'mouseOver' \| 'click' \| 'scrollIntoView' \| 'programmatic'` | Trigger event override |
|
|
96
96
|
| `outAction` | `'continue' \| 'pause' \| 'reset' \| 'reverse'` | Behaviour when trigger ends |
|
|
97
97
|
|
|
98
|
+
With none of `autoplay` / `play` / `pause` / `time` / `timeMs` set, the component renders the animation statically (initial state, no playback); use the template ref for imperative control.
|
|
99
|
+
|
|
100
|
+
Note: recreating the animator (unmount or a `doc` swap) emits `cancel`, `remove`, and `stop` for the torn-down instance. Scrubbing `time` / `timeMs` does **not** recreate the animator — it seeks the existing one.
|
|
101
|
+
|
|
98
102
|
## Events
|
|
99
103
|
|
|
100
104
|
| Event | Description |
|
|
@@ -103,4 +107,5 @@ Render a single frame at a specific point in time:
|
|
|
103
107
|
| `pause` | Animation paused |
|
|
104
108
|
| `cancel` | Animation cancelled |
|
|
105
109
|
| `finish` | Animation finished naturally |
|
|
106
|
-
| `remove` | Animation cleaned up |
|
|
110
|
+
| `remove` | Animation cleaned up (e.g. on unmount) |
|
|
111
|
+
| `stop` | Fired alongside any event that halts playback (`pause` / `cancel` / `finish` / `remove`) |
|
package/dist/index.cjs
CHANGED
|
@@ -96,22 +96,25 @@ function applyDocOverrides(doc, props, compMode) {
|
|
|
96
96
|
}
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
-
if (compMode === "fixedTime" /* fixedTime */) {
|
|
100
|
-
let seekDelay = 0;
|
|
101
|
-
if (props.time !== void 0) seekDelay = -props.time;
|
|
102
|
-
if (props.timeMs !== void 0) seekDelay = -props.timeMs;
|
|
103
|
-
const animator = doc.animator || {};
|
|
104
|
-
doc = { ...doc, animator: { ...animator, delay: seekDelay } };
|
|
105
|
-
}
|
|
106
99
|
return doc;
|
|
107
100
|
}
|
|
101
|
+
function calcSeekMs(doc, props) {
|
|
102
|
+
let seekMs;
|
|
103
|
+
const animator = doc.animator || {};
|
|
104
|
+
if (props.time !== void 0) {
|
|
105
|
+
const iterationsValue = props.iterations ?? animator.iterations;
|
|
106
|
+
const iterationsCount = typeof iterationsValue === "number" && iterationsValue >= 1 ? iterationsValue : 1;
|
|
107
|
+
const singleDuration = props.duration ?? animator.duration ?? 1e3;
|
|
108
|
+
seekMs = props.time * singleDuration * iterationsCount;
|
|
109
|
+
}
|
|
110
|
+
if (props.timeMs !== void 0) seekMs = props.timeMs;
|
|
111
|
+
return seekMs;
|
|
112
|
+
}
|
|
108
113
|
var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
|
|
109
114
|
name: "PixodeskSvgAnimator",
|
|
110
115
|
props: {
|
|
111
116
|
// -- Source
|
|
112
117
|
doc: { type: Object, required: true },
|
|
113
|
-
// -- Timeline
|
|
114
|
-
timeline: { type: String },
|
|
115
118
|
// -- Rendering mode
|
|
116
119
|
mode: { type: String },
|
|
117
120
|
// -- Timing overrides
|
|
@@ -133,14 +136,14 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
|
|
|
133
136
|
time: { type: Number },
|
|
134
137
|
timeMs: { type: Number }
|
|
135
138
|
},
|
|
136
|
-
emits: ["play", "stop", "pause", "cancel", "finish", "remove"
|
|
137
|
-
setup(props, { expose }) {
|
|
139
|
+
emits: ["play", "stop", "pause", "cancel", "finish", "remove"],
|
|
140
|
+
setup(props, { expose, emit }) {
|
|
138
141
|
const elementRefs = /* @__PURE__ */ new Map();
|
|
139
142
|
const apiRef = (0, import_vue.shallowRef)(null);
|
|
140
143
|
const compMode = (0, import_vue.computed)(() => {
|
|
141
144
|
if (props.autoplay) return "autoplay" /* autoplay */;
|
|
142
145
|
if (props.time !== void 0 || props.timeMs !== void 0) return "fixedTime" /* fixedTime */;
|
|
143
|
-
if (props.play !== void 0) return "play" /* play */;
|
|
146
|
+
if (props.play !== void 0 || props.pause !== void 0) return "play" /* play */;
|
|
144
147
|
return "static" /* static */;
|
|
145
148
|
});
|
|
146
149
|
const resolvedDoc = (0, import_vue.computed)(() => {
|
|
@@ -168,25 +171,59 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
|
|
|
168
171
|
destroyApi();
|
|
169
172
|
const doc = resolvedDoc.value;
|
|
170
173
|
if (!doc) return;
|
|
171
|
-
|
|
174
|
+
const callbacks = {
|
|
175
|
+
onPlay: () => emit("play"),
|
|
176
|
+
onPause: () => {
|
|
177
|
+
emit("pause");
|
|
178
|
+
emit("stop");
|
|
179
|
+
},
|
|
180
|
+
onCancel: () => {
|
|
181
|
+
emit("cancel");
|
|
182
|
+
emit("stop");
|
|
183
|
+
},
|
|
184
|
+
onFinish: () => {
|
|
185
|
+
emit("finish");
|
|
186
|
+
emit("stop");
|
|
187
|
+
},
|
|
188
|
+
onRemove: () => {
|
|
189
|
+
emit("remove");
|
|
190
|
+
emit("stop");
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
apiRef.value = (0, import_svg_animator_web.createAnimator)({ data: doc, adapter: createVueAdapter(elementRefs), callbacks });
|
|
194
|
+
syncPlayState();
|
|
195
|
+
applySeek();
|
|
172
196
|
}
|
|
173
197
|
function destroyApi() {
|
|
174
198
|
apiRef.value?.destroy();
|
|
175
199
|
apiRef.value = null;
|
|
176
200
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
201
|
+
function syncPlayState() {
|
|
202
|
+
if (compMode.value !== "play" /* play */) return;
|
|
203
|
+
if (props.play && !props.pause) {
|
|
204
|
+
apiRef.value?.play();
|
|
205
|
+
} else if (props.pause) {
|
|
206
|
+
apiRef.value?.pause();
|
|
207
|
+
} else if (props.play === false) {
|
|
208
|
+
apiRef.value?.finish();
|
|
209
|
+
} else {
|
|
210
|
+
apiRef.value?.play();
|
|
188
211
|
}
|
|
189
|
-
}
|
|
212
|
+
}
|
|
213
|
+
function applySeek() {
|
|
214
|
+
if (compMode.value !== "fixedTime" /* fixedTime */) return;
|
|
215
|
+
const doc = resolvedDoc.value;
|
|
216
|
+
if (!doc) return;
|
|
217
|
+
const seekMs = calcSeekMs(doc, props);
|
|
218
|
+
if (seekMs !== void 0) {
|
|
219
|
+
apiRef.value?.setCurrentTime(seekMs);
|
|
220
|
+
apiRef.value?.pause();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
(0, import_vue.onMounted)(() => createApi());
|
|
224
|
+
(0, import_vue.watch)(resolvedDoc, () => createApi(), { flush: "post" });
|
|
225
|
+
(0, import_vue.watch)([compMode, () => props.play, () => props.pause], () => syncPlayState());
|
|
226
|
+
(0, import_vue.watch)([compMode, () => props.time, () => props.timeMs], () => applySeek());
|
|
190
227
|
(0, import_vue.onUnmounted)(() => {
|
|
191
228
|
destroyApi();
|
|
192
229
|
});
|
|
@@ -196,7 +233,8 @@ var PixodeskSvgAnimator = (0, import_vue.defineComponent)({
|
|
|
196
233
|
pause: () => apiRef.value?.pause(),
|
|
197
234
|
cancel: () => apiRef.value?.cancel(),
|
|
198
235
|
finish: () => apiRef.value?.finish(),
|
|
199
|
-
|
|
236
|
+
setPlaybackRate: (rate) => apiRef.value?.setPlaybackRate(rate),
|
|
237
|
+
getCurrentTime: () => apiRef.value?.getCurrentTime() ?? null,
|
|
200
238
|
setCurrentTime: (time) => apiRef.value?.setCurrentTime(time)
|
|
201
239
|
};
|
|
202
240
|
expose(publicApi);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/PixodeskSvgAnimator.ts","../src/PixodeskSvgCssAnimator.ts"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport PixodeskSvgAnimator from './PixodeskSvgAnimator';\nexport { PixodeskSvgAnimator };\nexport type { VueAnimatorApi } from './PixodeskSvgAnimator';\n\nimport PixodeskSvgCssAnimator from './PixodeskSvgCssAnimator';\nexport { PixodeskSvgCssAnimator };","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport type { PxAnimatedSvgDocument, PxAnimatorAPI, PxNode, PxPlatformAdapter, PxTrigger } from '@pixodesk/svg-animator-web';\nimport { camelCaseToKebabWordIfNeeded, createAnimator, FillMode, generateNewIds, getNormalizedProps, STYLE_ATTR_NAMES } from '@pixodesk/svg-animator-web';\nimport {\n computed, defineComponent, h, onMounted, onUnmounted, ref, shallowRef, type PropType, type VNode,\n watch,\n} from 'vue';\n\n\n// -- Public types -----------------------------------------------------------\n\nexport interface VueAnimatorApi {\n /** Returns true if the animation is currently running. */\n isPlaying(): boolean;\n\n /** Starts or resumes the animation. */\n play(): void;\n\n /** Pauses the animation at its current state. */\n pause(): void;\n\n /** Stops the animation and resets it to its initial state. */\n cancel(): void;\n\n /** Jumps to the end of the animation and holds the final state. */\n finish(): void;\n\n /** Returns the current playback time in milliseconds. */\n getCurrentTime(): number | null;\n\n /** Jumps to a specific time (in milliseconds) in the animation. */\n setCurrentTime(time: number): void;\n}\n\n\n// -- Internal types ---------------------------------------------------------\n\nenum CompMode {\n static = 'static',\n autoplay = 'autoplay',\n play = 'play',\n fixedTime = 'fixedTime'\n}\n\n\n// -- Vue ↔ Animator bridge --------------------------------------------------\n\n/**\n * Creates a platform adapter that routes animator attribute updates\n * to the corresponding Vue-managed DOM element refs.\n */\nfunction createVueAdapter(elementRefs: Map<string, Element>) {\n const warnedSelectors = new Set<string>();\n\n const adapter: PxPlatformAdapter = {\n isConnected: () => true,\n setAttribute: (id, attrName, value) => {\n attrName = camelCaseToKebabWordIfNeeded(attrName);\n\n const element = elementRefs.get(id);\n\n if (!element && !warnedSelectors.has(id)) {\n warnedSelectors.add(id);\n console.warn('setAttribute: No elements found for id \"' + id + '\"');\n }\n\n if (element) {\n element.setAttribute(attrName, value);\n if (STYLE_ATTR_NAMES.has(attrName)) {\n (element as HTMLElement).style[attrName as any] = value;\n }\n }\n },\n };\n return adapter;\n}\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Helper: apply doc overrides --------------------------------------------\n\ninterface DocOverrideProps {\n mode?: 'webapi' | 'frames' | 'auto';\n delay?: number;\n fill?: FillMode;\n iterations?: number | 'infinite';\n duration?: number;\n direction?: PlaybackDirection;\n frameRate?: number;\n startOn?: 'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic';\n outAction?: 'continue' | 'pause' | 'reset' | 'reverse';\n scrollIntoViewThreshold?: number;\n time?: number;\n timeMs?: number;\n}\n\nfunction applyDocOverrides(\n doc: PxAnimatedSvgDocument,\n props: DocOverrideProps,\n compMode: CompMode,\n): PxAnimatedSvgDocument {\n\n // In non-autoplay modes, override the document trigger to 'programmatic'\n // so the component can manage playback itself.\n if (compMode !== CompMode.autoplay) {\n const docStartOn = doc.animator?.trigger?.startOn;\n if (docStartOn && docStartOn !== 'programmatic') { // FIXME: use enum\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: { ...doc.animator?.trigger, startOn: 'programmatic' }\n }\n };\n }\n }\n\n // Apply timing overrides from props onto the document config.\n const { mode, duration, delay, iterations, fill, direction, frameRate } = props;\n if (\n mode !== undefined || duration !== undefined || delay !== undefined ||\n iterations !== undefined || fill !== undefined || direction !== undefined ||\n frameRate !== undefined\n ) {\n const animator = doc.animator || {};\n doc = {\n ...doc,\n animator: {\n ...animator,\n mode: mode !== undefined ? mode : animator.mode,\n duration: duration !== undefined ? duration : animator.duration,\n delay: delay !== undefined ? delay : animator.delay,\n iterations: iterations !== undefined ? iterations : animator.iterations,\n fill: fill !== undefined ? fill : animator.fill,\n direction: direction !== undefined ? direction : animator.direction,\n frameRate: frameRate !== undefined ? frameRate : animator.frameRate,\n }\n };\n }\n\n // Apply trigger overrides from props.\n const { startOn, outAction, scrollIntoViewThreshold } = props;\n if (startOn !== undefined || outAction !== undefined || scrollIntoViewThreshold !== undefined) {\n const trigger: PxTrigger = doc.animator?.trigger || {};\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: {\n ...trigger,\n startOn: startOn !== undefined ? startOn : trigger.startOn,\n outAction: outAction !== undefined ? outAction : trigger.outAction,\n scrollIntoViewThreshold: scrollIntoViewThreshold !== undefined ? scrollIntoViewThreshold : trigger.scrollIntoViewThreshold,\n }\n }\n };\n }\n\n // In controlled-time mode, use a negative delay to seek to the given frame.\n if (compMode === CompMode.fixedTime) {\n let seekDelay = 0;\n if (props.time !== undefined) seekDelay = -props.time; // FIXME: time as a fraction of total duration?\n if (props.timeMs !== undefined) seekDelay = -props.timeMs;\n const animator = doc.animator || {};\n doc = { ...doc, animator: { ...animator, delay: seekDelay } };\n }\n\n return doc;\n}\n\n\n// -- Main public component --------------------------------------------------\n\n/**\n * Vue component for rendering and controlling Pixodesk SVG animations.\n *\n * Supports four mutually-exclusive control modes:\n *\n * 1. **Autoplay** – uses triggers from the animation document.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" autoplay />\n * ```\n *\n * 2. **Declarative play/pause** – controlled via boolean props.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" play :pause=\"false\" />\n * ```\n *\n * 3. **Imperative** – exposes a ref-based API for full programmatic control.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" ref=\"animator\" />\n * <button @click=\"$refs.animator.play()\">Play</button>\n * ```\n *\n * 4. **Controlled time** – renders a single frame at a given time.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" :time=\"0.5\" />\n * <PixodeskSvgAnimator :doc=\"animation\" :timeMs=\"500\" />\n * ```\n */\nconst PixodeskSvgAnimator = defineComponent({\n name: 'PixodeskSvgAnimator',\n\n props: {\n // -- Source\n doc: { type: Object as PropType<PxAnimatedSvgDocument>, required: true },\n\n // -- Timeline\n timeline: { type: String as PropType<'time' | 'scroll'> },\n\n // -- Rendering mode\n mode: { type: String as PropType<'webapi' | 'frames' | 'auto'> },\n\n // -- Timing overrides\n delay: { type: Number },\n fill: { type: String as PropType<FillMode> },\n iterations: { type: [Number, String] as PropType<number | 'infinite'> },\n duration: { type: Number },\n direction: { type: String as PropType<PlaybackDirection> },\n frameRate: { type: Number },\n\n // -- Trigger overrides\n startOn: { type: String as PropType<'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic'> },\n outAction: { type: String as PropType<'continue' | 'pause' | 'reset' | 'reverse'> },\n scrollIntoViewThreshold: { type: Number },\n\n // -- Declarative control\n autoplay: { type: Boolean, default: undefined },\n play: { type: Boolean, default: undefined },\n pause: { type: Boolean, default: undefined },\n\n // -- Controlled time\n time: { type: Number },\n timeMs: { type: Number },\n },\n\n emits: ['play', 'stop', 'pause', 'cancel', 'finish', 'remove', 'warning', 'error'],\n\n setup(props, { expose }) {\n const elementRefs = new Map<string, Element>();\n const apiRef = shallowRef<PxAnimatorAPI | null>(null);\n\n // -- Determine control mode ---------------------------------------------\n\n const compMode = computed<CompMode>(() => {\n if (props.autoplay) return CompMode.autoplay;\n if (props.time !== undefined || props.timeMs !== undefined) return CompMode.fixedTime;\n if (props.play !== undefined) return CompMode.play;\n return CompMode.static;\n });\n\n // -- Prepare the document with overrides --------------------------------\n\n const resolvedDoc = computed(() => {\n let doc = generateNewIds(props.doc);\n return applyDocOverrides(doc, props, compMode.value);\n });\n\n // -- Render the SVG node tree -------------------------------------------\n\n function renderNode(node: PxNode | undefined): VNode | null {\n if (!node) return null;\n\n const { type, animate, meta, children, ...attrs } = node;\n const normProps = getNormalizedProps(attrs);\n\n // Capture a ref to each element with an id.\n if (node['id']) {\n const nodeId = node['id'];\n normProps['ref'] = (el: Element | null) => {\n if (el) {\n elementRefs.set(nodeId, el);\n } else {\n elementRefs.delete(nodeId);\n }\n };\n }\n\n const childVNodes = children?.map(child => renderNode(child)).filter(Boolean) as VNode[] | undefined;\n return h(type, normProps, childVNodes);\n }\n\n // -- Animator lifecycle -------------------------------------------------\n\n function createApi() {\n destroyApi();\n const doc = resolvedDoc.value;\n if (!doc) return;\n apiRef.value = createAnimator({ data: doc, adapter: createVueAdapter(elementRefs) });\n }\n\n function destroyApi() {\n apiRef.value?.destroy();\n apiRef.value = null;\n }\n\n // Create the animator once DOM refs are available.\n onMounted(() => createApi());\n\n // Recreate the animator when the resolved doc changes.\n watch(resolvedDoc, () => createApi());\n\n // Sync declarative play/pause props with the animator.\n watch([compMode, () => props.play, () => props.pause], () => {\n if (compMode.value === CompMode.play) {\n if (props.play && !props.pause) {\n apiRef.value?.play();\n } else if (props.pause) {\n apiRef.value?.pause();\n } else {\n apiRef.value?.finish();\n }\n }\n });\n\n onUnmounted(() => {\n destroyApi();\n });\n\n // -- Expose imperative API ----------------------------------------------\n\n const publicApi: VueAnimatorApi = {\n isPlaying: () => apiRef.value?.isPlaying() || false,\n play: () => apiRef.value?.play(),\n pause: () => apiRef.value?.pause(),\n cancel: () => apiRef.value?.cancel(),\n finish: () => apiRef.value?.finish(),\n getCurrentTime: () => apiRef.value?.getCurrentTime() || null,\n setCurrentTime: (time: number) => apiRef.value?.setCurrentTime(time),\n };\n\n expose(publicApi);\n\n // -- Render -------------------------------------------------------------\n\n return () => {\n const doc = resolvedDoc.value;\n return doc ? renderNode(doc) : null;\n };\n },\n});\n\nexport default PixodeskSvgAnimator;\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { OutAction, StartOn } from \"@pixodesk/svg-animator-web\";\nimport { computed, defineComponent, h, onMounted, onUnmounted, ref, useAttrs, type PropType } from 'vue';\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n\n/**\n * Controls playback of a SVG+CSS animated file by toggling class names on a wrapper div.\n *\n * Intended for use with SVG files exported from the Pixodesk editor using the\n * **CSS Keyframes** flavor (no `<script>` tag). Import the SVG as a Vue component\n * via `vite-svg-loader` and pass it as the default slot:\n *\n * ```vue\n * <script setup>\n * import AnimationSvg from './animation.svg'; // vite-svg-loader\n * </script>\n *\n * <template>\n * <PixodeskSvgCssAnimator startOn=\"mouseOver\" outAction=\"pause\">\n * <AnimationSvg />\n * </PixodeskSvgCssAnimator>\n * </template>\n * ```\n *\n * The wrapper div carries one of three animation states via CSS class names:\n * - *(no class)* — idle, animation not started\n * - `px-anim-enabled` — started but paused\n * - `px-anim-enabled px-anim-playing` — actively playing\n *\n * @prop startOn - What triggers the animation to start:\n * - `'load'` — plays immediately on mount (default)\n * - `'mouseOver'` — plays on hover\n * - `'click'` — plays on click, toggles on second click\n * - `'scrollIntoView'` — plays when the element enters the viewport\n * @prop outAction - What happens when the trigger ends (hover/scroll out, second click):\n * - `'continue'` — keeps playing (default)\n * - `'pause'` — pauses at the current frame\n * - `'reset'` — resets to the beginning\n */\nconst PixodeskSvgCssAnimator = defineComponent({\n name: 'PixodeskSvgCssAnimator',\n\n inheritAttrs: false,\n\n props: {\n startOn: { type: String as PropType<StartOn>, default: 'load' },\n outAction: { type: String as PropType<OutAction>, default: 'continue' },\n },\n\n setup(props, { slots }) {\n const attrs = useAttrs();\n const state = ref<AnimState>(props.startOn === 'load' ? 'playing' : 'idle');\n const divRef = ref<HTMLDivElement | null>(null);\n\n const goOut = () => {\n state.value =\n props.outAction === 'reset' ? 'idle' :\n props.outAction === 'pause' ? 'paused' : 'playing';\n };\n\n let observerCleanup: (() => void) | undefined;\n\n onMounted(() => {\n if (props.startOn !== 'scrollIntoView') return;\n const el = divRef.value;\n if (!el) return;\n const outState: AnimState =\n props.outAction === 'reset' ? 'idle' :\n props.outAction === 'pause' ? 'paused' : 'playing';\n const observer = new IntersectionObserver(\n ([entry]) => { state.value = entry.isIntersecting ? 'playing' : outState; },\n { threshold: 0.1 }\n );\n observer.observe(el);\n observerCleanup = () => observer.disconnect();\n });\n\n onUnmounted(() => observerCleanup?.());\n\n const animClass = computed(() =>\n state.value === 'playing' ? 'px-anim-enabled px-anim-playing' :\n state.value === 'paused' ? 'px-anim-enabled' : ''\n );\n\n const handlers = computed(() =>\n props.startOn === 'mouseOver' ? {\n onMouseenter: () => { state.value = 'playing'; },\n onMouseleave: goOut,\n } :\n props.startOn === 'click' ? {\n onClick: () => state.value === 'playing' ? goOut() : (state.value = 'playing'),\n } : {}\n );\n\n return () => h('div', {\n ref: divRef,\n ...attrs,\n class: [attrs.class, animClass.value],\n ...handlers.value,\n }, slots.default?.());\n },\n});\n\nexport default PixodeskSvgCssAnimator;\n"],"mappings":"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","names":["import_vue"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/PixodeskSvgAnimator.ts","../src/PixodeskSvgCssAnimator.ts"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport PixodeskSvgAnimator from './PixodeskSvgAnimator';\nexport { PixodeskSvgAnimator };\nexport type { VueAnimatorApi } from './PixodeskSvgAnimator';\n\nimport PixodeskSvgCssAnimator from './PixodeskSvgCssAnimator';\nexport { PixodeskSvgCssAnimator };","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport type { PxAnimatedSvgDocument, PxAnimatorAPI, PxNode, PxPlatformAdapter, PxTrigger } from '@pixodesk/svg-animator-web';\nimport { camelCaseToKebabWordIfNeeded, createAnimator, FillMode, generateNewIds, getNormalizedProps, STYLE_ATTR_NAMES } from '@pixodesk/svg-animator-web';\nimport {\n computed, defineComponent, h, onMounted, onUnmounted, ref, shallowRef, type PropType, type VNode,\n watch,\n} from 'vue';\n\n\n// -- Public types -----------------------------------------------------------\n\nexport interface VueAnimatorApi {\n /** Returns true if the animation is currently running. */\n isPlaying(): boolean;\n\n /** Starts or resumes the animation. */\n play(): void;\n\n /** Pauses the animation at its current state. */\n pause(): void;\n\n /** Stops the animation and resets it to its initial state. */\n cancel(): void;\n\n /** Jumps to the end of the animation and holds the final state. */\n finish(): void;\n\n /** Changes the speed of the animation. 1 is normal, 2 is double, -1 is reverse. */\n setPlaybackRate(rate: number): void;\n\n /** Returns the current playback time in milliseconds. */\n getCurrentTime(): number | null;\n\n /** Jumps to a specific time (in milliseconds) in the animation. */\n setCurrentTime(time: number): void;\n}\n\n\n// -- Internal types ---------------------------------------------------------\n\nenum CompMode {\n static = 'static',\n autoplay = 'autoplay',\n play = 'play',\n fixedTime = 'fixedTime'\n}\n\n\n// -- Vue ↔ Animator bridge --------------------------------------------------\n\n/**\n * Creates a platform adapter that routes animator attribute updates\n * to the corresponding Vue-managed DOM element refs.\n */\nfunction createVueAdapter(elementRefs: Map<string, Element>) {\n const warnedSelectors = new Set<string>();\n\n const adapter: PxPlatformAdapter = {\n isConnected: () => true,\n setAttribute: (id, attrName, value) => {\n attrName = camelCaseToKebabWordIfNeeded(attrName);\n\n const element = elementRefs.get(id);\n\n if (!element && !warnedSelectors.has(id)) {\n warnedSelectors.add(id);\n console.warn('setAttribute: No elements found for id \"' + id + '\"');\n }\n\n if (element) {\n element.setAttribute(attrName, value);\n if (STYLE_ATTR_NAMES.has(attrName)) {\n (element as HTMLElement).style[attrName as any] = value;\n }\n }\n },\n };\n return adapter;\n}\n\n// FIXME: add model validation (e.g. isElementFileJson check)\n\n\n// -- Helper: apply doc overrides --------------------------------------------\n\ninterface DocOverrideProps {\n mode?: 'webapi' | 'frames' | 'auto';\n delay?: number;\n fill?: FillMode;\n iterations?: number | 'infinite';\n duration?: number;\n direction?: PlaybackDirection;\n frameRate?: number;\n startOn?: 'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic';\n outAction?: 'continue' | 'pause' | 'reset' | 'reverse';\n scrollIntoViewThreshold?: number;\n time?: number;\n timeMs?: number;\n}\n\nfunction applyDocOverrides(\n doc: PxAnimatedSvgDocument,\n props: DocOverrideProps,\n compMode: CompMode,\n): PxAnimatedSvgDocument {\n\n // In non-autoplay modes, override the document trigger to 'programmatic'\n // so the component can manage playback itself.\n if (compMode !== CompMode.autoplay) {\n const docStartOn = doc.animator?.trigger?.startOn;\n if (docStartOn && docStartOn !== 'programmatic') { // FIXME: use enum\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: { ...doc.animator?.trigger, startOn: 'programmatic' }\n }\n };\n }\n }\n\n // Apply timing overrides from props onto the document config.\n const { mode, duration, delay, iterations, fill, direction, frameRate } = props;\n if (\n mode !== undefined || duration !== undefined || delay !== undefined ||\n iterations !== undefined || fill !== undefined || direction !== undefined ||\n frameRate !== undefined\n ) {\n const animator = doc.animator || {};\n doc = {\n ...doc,\n animator: {\n ...animator,\n mode: mode !== undefined ? mode : animator.mode,\n duration: duration !== undefined ? duration : animator.duration,\n delay: delay !== undefined ? delay : animator.delay,\n iterations: iterations !== undefined ? iterations : animator.iterations,\n fill: fill !== undefined ? fill : animator.fill,\n direction: direction !== undefined ? direction : animator.direction,\n frameRate: frameRate !== undefined ? frameRate : animator.frameRate,\n }\n };\n }\n\n // Apply trigger overrides from props.\n const { startOn, outAction, scrollIntoViewThreshold } = props;\n if (startOn !== undefined || outAction !== undefined || scrollIntoViewThreshold !== undefined) {\n const trigger: PxTrigger = doc.animator?.trigger || {};\n doc = {\n ...doc,\n animator: {\n ...doc.animator,\n trigger: {\n ...trigger,\n startOn: startOn !== undefined ? startOn : trigger.startOn,\n outAction: outAction !== undefined ? outAction : trigger.outAction,\n scrollIntoViewThreshold: scrollIntoViewThreshold !== undefined ? scrollIntoViewThreshold : trigger.scrollIntoViewThreshold,\n }\n }\n };\n }\n\n return doc;\n}\n\n/**\n * Controlled-time mode: absolute seek target in ms. `time` is a fraction\n * (0–1) of the WHOLE timeline (duration × iterations); `timeMs` is absolute.\n * Applied through the animator API (setCurrentTime) so scrubbing does NOT\n * recreate the animator.\n */\nfunction calcSeekMs(doc: PxAnimatedSvgDocument, props: DocOverrideProps): number | undefined {\n let seekMs: number | undefined;\n const animator = doc.animator || {};\n if (props.time !== undefined) {\n const iterationsValue = props.iterations ?? animator.iterations;\n const iterationsCount = typeof iterationsValue === 'number' && iterationsValue >= 1 ? iterationsValue : 1;\n const singleDuration = props.duration ?? animator.duration ?? 1000; // engine default duration\n seekMs = props.time * singleDuration * iterationsCount;\n }\n if (props.timeMs !== undefined) seekMs = props.timeMs;\n return seekMs;\n}\n\n\n// -- Main public component --------------------------------------------------\n\n/**\n * Vue component for rendering and controlling Pixodesk SVG animations.\n *\n * Supports four mutually-exclusive control modes:\n *\n * 1. **Autoplay** – uses triggers from the animation document.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" autoplay />\n * ```\n *\n * 2. **Declarative play/pause** – controlled via boolean props.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" play :pause=\"false\" />\n * ```\n *\n * 3. **Imperative** – exposes a ref-based API for full programmatic control.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" ref=\"animator\" />\n * <button @click=\"$refs.animator.play()\">Play</button>\n * ```\n *\n * 4. **Controlled time** – renders a single frame at a given time.\n * ```vue\n * <PixodeskSvgAnimator :doc=\"animation\" :time=\"0.5\" />\n * <PixodeskSvgAnimator :doc=\"animation\" :timeMs=\"500\" />\n * ```\n */\nconst PixodeskSvgAnimator = defineComponent({\n name: 'PixodeskSvgAnimator',\n\n props: {\n // -- Source\n doc: { type: Object as PropType<PxAnimatedSvgDocument>, required: true },\n\n // -- Rendering mode\n mode: { type: String as PropType<'webapi' | 'frames' | 'auto'> },\n\n // -- Timing overrides\n delay: { type: Number },\n fill: { type: String as PropType<FillMode> },\n iterations: { type: [Number, String] as PropType<number | 'infinite'> },\n duration: { type: Number },\n direction: { type: String as PropType<PlaybackDirection> },\n frameRate: { type: Number },\n\n // -- Trigger overrides\n startOn: { type: String as PropType<'load' | 'mouseOver' | 'click' | 'scrollIntoView' | 'programmatic'> },\n outAction: { type: String as PropType<'continue' | 'pause' | 'reset' | 'reverse'> },\n scrollIntoViewThreshold: { type: Number },\n\n // -- Declarative control\n autoplay: { type: Boolean, default: undefined },\n play: { type: Boolean, default: undefined },\n pause: { type: Boolean, default: undefined },\n\n // -- Controlled time\n time: { type: Number },\n timeMs: { type: Number },\n },\n\n emits: ['play', 'stop', 'pause', 'cancel', 'finish', 'remove'],\n\n setup(props, { expose, emit }) {\n const elementRefs = new Map<string, Element>();\n const apiRef = shallowRef<PxAnimatorAPI | null>(null);\n\n // -- Determine control mode ---------------------------------------------\n\n const compMode = computed<CompMode>(() => {\n if (props.autoplay) return CompMode.autoplay;\n if (props.time !== undefined || props.timeMs !== undefined) return CompMode.fixedTime;\n if (props.play !== undefined || props.pause !== undefined) return CompMode.play;\n return CompMode.static;\n });\n\n // -- Prepare the document with overrides --------------------------------\n\n const resolvedDoc = computed(() => {\n let doc = generateNewIds(props.doc);\n return applyDocOverrides(doc, props, compMode.value);\n });\n\n // -- Render the SVG node tree -------------------------------------------\n\n function renderNode(node: PxNode | undefined): VNode | null {\n if (!node) return null;\n\n const { type, animate, meta, children, ...attrs } = node;\n const normProps = getNormalizedProps(attrs);\n\n // Capture a ref to each element with an id.\n if (node['id']) {\n const nodeId = node['id'];\n normProps['ref'] = (el: Element | null) => {\n if (el) {\n elementRefs.set(nodeId, el);\n } else {\n elementRefs.delete(nodeId);\n }\n };\n }\n\n const childVNodes = children?.map(child => renderNode(child)).filter(Boolean) as VNode[] | undefined;\n return h(type, normProps, childVNodes);\n }\n\n // -- Animator lifecycle -------------------------------------------------\n\n function createApi() {\n destroyApi();\n const doc = resolvedDoc.value;\n if (!doc) return;\n\n // Route animator lifecycle events to Vue component events.\n // `stop` fires alongside any event that halts playback.\n const callbacks = {\n onPlay: () => emit('play'),\n onPause: () => { emit('pause'); emit('stop'); },\n onCancel: () => { emit('cancel'); emit('stop'); },\n onFinish: () => { emit('finish'); emit('stop'); },\n onRemove: () => { emit('remove'); emit('stop'); },\n };\n\n apiRef.value = createAnimator({ data: doc, adapter: createVueAdapter(elementRefs), callbacks });\n\n // (Re)apply the declarative control state to the fresh animator —\n // covers both the initial mount (e.g. `:play=\"true\"` from the\n // start) and doc swaps.\n syncPlayState();\n applySeek();\n }\n\n function destroyApi() {\n apiRef.value?.destroy();\n apiRef.value = null;\n }\n\n /** Declarative play/pause → animator calls. */\n function syncPlayState() {\n if (compMode.value !== CompMode.play) return;\n if (props.play && !props.pause) {\n apiRef.value?.play();\n } else if (props.pause) {\n apiRef.value?.pause();\n } else if (props.play === false) {\n // explicit play=false → jump to the end state\n apiRef.value?.finish();\n } else {\n // pause-only usage: pause switched off → resume\n apiRef.value?.play();\n }\n }\n\n /** Controlled-time mode: seek through the animator API (no recreate). */\n function applySeek() {\n if (compMode.value !== CompMode.fixedTime) return;\n const doc = resolvedDoc.value;\n if (!doc) return;\n const seekMs = calcSeekMs(doc, props);\n if (seekMs !== undefined) {\n apiRef.value?.setCurrentTime(seekMs);\n apiRef.value?.pause();\n }\n }\n\n // Create the animator once DOM refs are available.\n onMounted(() => createApi());\n\n // Recreate the animator when the resolved doc changes.\n // `flush: 'post'` — the animator must be created AFTER the DOM is\n // patched, otherwise the new root element isn't in the document yet\n // and trigger setup fails (autoplay would never resume after a doc swap).\n watch(resolvedDoc, () => createApi(), { flush: 'post' });\n\n // Sync declarative play/pause props with the animator.\n watch([compMode, () => props.play, () => props.pause], () => syncPlayState());\n\n // Scrubbing time/timeMs only seeks — the animator is NOT recreated.\n watch([compMode, () => props.time, () => props.timeMs], () => applySeek());\n\n onUnmounted(() => {\n destroyApi();\n });\n\n // -- Expose imperative API ----------------------------------------------\n\n const publicApi: VueAnimatorApi = {\n isPlaying: () => apiRef.value?.isPlaying() || false,\n play: () => apiRef.value?.play(),\n pause: () => apiRef.value?.pause(),\n cancel: () => apiRef.value?.cancel(),\n finish: () => apiRef.value?.finish(),\n setPlaybackRate: (rate: number) => apiRef.value?.setPlaybackRate(rate),\n getCurrentTime: () => apiRef.value?.getCurrentTime() ?? null,\n setCurrentTime: (time: number) => apiRef.value?.setCurrentTime(time),\n };\n\n expose(publicApi);\n\n // -- Render -------------------------------------------------------------\n\n return () => {\n const doc = resolvedDoc.value;\n return doc ? renderNode(doc) : null;\n };\n },\n});\n\nexport default PixodeskSvgAnimator;\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { OutAction, StartOn } from \"@pixodesk/svg-animator-web\";\nimport { computed, defineComponent, h, onMounted, onUnmounted, ref, useAttrs, type PropType } from 'vue';\n\n\ntype AnimState = 'idle' | 'paused' | 'playing';\n\n\n/**\n * Controls playback of a SVG+CSS animated file by toggling class names on a wrapper div.\n *\n * Intended for use with SVG files exported from the Pixodesk editor using the\n * **CSS Keyframes** flavor (no `<script>` tag). Import the SVG as a Vue component\n * via `vite-svg-loader` and pass it as the default slot:\n *\n * ```vue\n * <script setup>\n * import AnimationSvg from './animation.svg'; // vite-svg-loader\n * </script>\n *\n * <template>\n * <PixodeskSvgCssAnimator startOn=\"mouseOver\" outAction=\"pause\">\n * <AnimationSvg />\n * </PixodeskSvgCssAnimator>\n * </template>\n * ```\n *\n * The wrapper div carries one of three animation states via CSS class names:\n * - *(no class)* — idle, animation not started\n * - `px-anim-enabled` — started but paused\n * - `px-anim-enabled px-anim-playing` — actively playing\n *\n * @prop startOn - What triggers the animation to start:\n * - `'load'` — plays immediately on mount (default)\n * - `'mouseOver'` — plays on hover\n * - `'click'` — plays on click, toggles on second click\n * - `'scrollIntoView'` — plays when the element enters the viewport\n * @prop outAction - What happens when the trigger ends (hover/scroll out, second click):\n * - `'continue'` — keeps playing (default)\n * - `'pause'` — pauses at the current frame\n * - `'reset'` — resets to the beginning\n */\nconst PixodeskSvgCssAnimator = defineComponent({\n name: 'PixodeskSvgCssAnimator',\n\n inheritAttrs: false,\n\n props: {\n startOn: { type: String as PropType<StartOn>, default: 'load' },\n outAction: { type: String as PropType<OutAction>, default: 'continue' },\n },\n\n setup(props, { slots }) {\n const attrs = useAttrs();\n const state = ref<AnimState>(props.startOn === 'load' ? 'playing' : 'idle');\n const divRef = ref<HTMLDivElement | null>(null);\n\n const goOut = () => {\n state.value =\n props.outAction === 'reset' ? 'idle' :\n props.outAction === 'pause' ? 'paused' : 'playing';\n };\n\n let observerCleanup: (() => void) | undefined;\n\n onMounted(() => {\n if (props.startOn !== 'scrollIntoView') return;\n const el = divRef.value;\n if (!el) return;\n const outState: AnimState =\n props.outAction === 'reset' ? 'idle' :\n props.outAction === 'pause' ? 'paused' : 'playing';\n const observer = new IntersectionObserver(\n ([entry]) => { state.value = entry.isIntersecting ? 'playing' : outState; },\n { threshold: 0.1 }\n );\n observer.observe(el);\n observerCleanup = () => observer.disconnect();\n });\n\n onUnmounted(() => observerCleanup?.());\n\n const animClass = computed(() =>\n state.value === 'playing' ? 'px-anim-enabled px-anim-playing' :\n state.value === 'paused' ? 'px-anim-enabled' : ''\n );\n\n const handlers = computed(() =>\n props.startOn === 'mouseOver' ? {\n onMouseenter: () => { state.value = 'playing'; },\n onMouseleave: goOut,\n } :\n props.startOn === 'click' ? {\n onClick: () => state.value === 'playing' ? goOut() : (state.value = 'playing'),\n } : {}\n );\n\n return () => h('div', {\n ref: divRef,\n ...attrs,\n class: [attrs.class, animClass.value],\n ...handlers.value,\n }, slots.default?.());\n },\n});\n\nexport default PixodeskSvgCssAnimator;\n"],"mappings":"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","names":["import_vue"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -13,6 +13,8 @@ interface VueAnimatorApi {
|
|
|
13
13
|
cancel(): void;
|
|
14
14
|
/** Jumps to the end of the animation and holds the final state. */
|
|
15
15
|
finish(): void;
|
|
16
|
+
/** Changes the speed of the animation. 1 is normal, 2 is double, -1 is reverse. */
|
|
17
|
+
setPlaybackRate(rate: number): void;
|
|
16
18
|
/** Returns the current playback time in milliseconds. */
|
|
17
19
|
getCurrentTime(): number | null;
|
|
18
20
|
/** Jumps to a specific time (in milliseconds) in the animation. */
|
|
@@ -50,9 +52,6 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
50
52
|
type: PropType<PxAnimatedSvgDocument>;
|
|
51
53
|
required: true;
|
|
52
54
|
};
|
|
53
|
-
timeline: {
|
|
54
|
-
type: PropType<"time" | "scroll">;
|
|
55
|
-
};
|
|
56
55
|
mode: {
|
|
57
56
|
type: PropType<"webapi" | "frames" | "auto">;
|
|
58
57
|
};
|
|
@@ -103,14 +102,11 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
103
102
|
};
|
|
104
103
|
}>, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
105
104
|
[key: string]: any;
|
|
106
|
-
}> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("play" | "pause" | "stop" | "cancel" | "finish" | "remove"
|
|
105
|
+
}> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("play" | "pause" | "stop" | "cancel" | "finish" | "remove")[], "play" | "pause" | "stop" | "cancel" | "finish" | "remove", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
107
106
|
doc: {
|
|
108
107
|
type: PropType<PxAnimatedSvgDocument>;
|
|
109
108
|
required: true;
|
|
110
109
|
};
|
|
111
|
-
timeline: {
|
|
112
|
-
type: PropType<"time" | "scroll">;
|
|
113
|
-
};
|
|
114
110
|
mode: {
|
|
115
111
|
type: PropType<"webapi" | "frames" | "auto">;
|
|
116
112
|
};
|
|
@@ -166,8 +162,6 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
166
162
|
onCancel?: ((...args: any[]) => any) | undefined;
|
|
167
163
|
onFinish?: ((...args: any[]) => any) | undefined;
|
|
168
164
|
onRemove?: ((...args: any[]) => any) | undefined;
|
|
169
|
-
onWarning?: ((...args: any[]) => any) | undefined;
|
|
170
|
-
onError?: ((...args: any[]) => any) | undefined;
|
|
171
165
|
}>, {
|
|
172
166
|
autoplay: boolean;
|
|
173
167
|
play: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ interface VueAnimatorApi {
|
|
|
13
13
|
cancel(): void;
|
|
14
14
|
/** Jumps to the end of the animation and holds the final state. */
|
|
15
15
|
finish(): void;
|
|
16
|
+
/** Changes the speed of the animation. 1 is normal, 2 is double, -1 is reverse. */
|
|
17
|
+
setPlaybackRate(rate: number): void;
|
|
16
18
|
/** Returns the current playback time in milliseconds. */
|
|
17
19
|
getCurrentTime(): number | null;
|
|
18
20
|
/** Jumps to a specific time (in milliseconds) in the animation. */
|
|
@@ -50,9 +52,6 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
50
52
|
type: PropType<PxAnimatedSvgDocument>;
|
|
51
53
|
required: true;
|
|
52
54
|
};
|
|
53
|
-
timeline: {
|
|
54
|
-
type: PropType<"time" | "scroll">;
|
|
55
|
-
};
|
|
56
55
|
mode: {
|
|
57
56
|
type: PropType<"webapi" | "frames" | "auto">;
|
|
58
57
|
};
|
|
@@ -103,14 +102,11 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
103
102
|
};
|
|
104
103
|
}>, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
105
104
|
[key: string]: any;
|
|
106
|
-
}> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("play" | "pause" | "stop" | "cancel" | "finish" | "remove"
|
|
105
|
+
}> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("play" | "pause" | "stop" | "cancel" | "finish" | "remove")[], "play" | "pause" | "stop" | "cancel" | "finish" | "remove", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
107
106
|
doc: {
|
|
108
107
|
type: PropType<PxAnimatedSvgDocument>;
|
|
109
108
|
required: true;
|
|
110
109
|
};
|
|
111
|
-
timeline: {
|
|
112
|
-
type: PropType<"time" | "scroll">;
|
|
113
|
-
};
|
|
114
110
|
mode: {
|
|
115
111
|
type: PropType<"webapi" | "frames" | "auto">;
|
|
116
112
|
};
|
|
@@ -166,8 +162,6 @@ declare const PixodeskSvgAnimator: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
166
162
|
onCancel?: ((...args: any[]) => any) | undefined;
|
|
167
163
|
onFinish?: ((...args: any[]) => any) | undefined;
|
|
168
164
|
onRemove?: ((...args: any[]) => any) | undefined;
|
|
169
|
-
onWarning?: ((...args: any[]) => any) | undefined;
|
|
170
|
-
onError?: ((...args: any[]) => any) | undefined;
|
|
171
165
|
}>, {
|
|
172
166
|
autoplay: boolean;
|
|
173
167
|
play: boolean;
|
package/dist/index.js
CHANGED
|
@@ -78,22 +78,25 @@ function applyDocOverrides(doc, props, compMode) {
|
|
|
78
78
|
}
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
-
if (compMode === "fixedTime" /* fixedTime */) {
|
|
82
|
-
let seekDelay = 0;
|
|
83
|
-
if (props.time !== void 0) seekDelay = -props.time;
|
|
84
|
-
if (props.timeMs !== void 0) seekDelay = -props.timeMs;
|
|
85
|
-
const animator = doc.animator || {};
|
|
86
|
-
doc = { ...doc, animator: { ...animator, delay: seekDelay } };
|
|
87
|
-
}
|
|
88
81
|
return doc;
|
|
89
82
|
}
|
|
83
|
+
function calcSeekMs(doc, props) {
|
|
84
|
+
let seekMs;
|
|
85
|
+
const animator = doc.animator || {};
|
|
86
|
+
if (props.time !== void 0) {
|
|
87
|
+
const iterationsValue = props.iterations ?? animator.iterations;
|
|
88
|
+
const iterationsCount = typeof iterationsValue === "number" && iterationsValue >= 1 ? iterationsValue : 1;
|
|
89
|
+
const singleDuration = props.duration ?? animator.duration ?? 1e3;
|
|
90
|
+
seekMs = props.time * singleDuration * iterationsCount;
|
|
91
|
+
}
|
|
92
|
+
if (props.timeMs !== void 0) seekMs = props.timeMs;
|
|
93
|
+
return seekMs;
|
|
94
|
+
}
|
|
90
95
|
var PixodeskSvgAnimator = defineComponent({
|
|
91
96
|
name: "PixodeskSvgAnimator",
|
|
92
97
|
props: {
|
|
93
98
|
// -- Source
|
|
94
99
|
doc: { type: Object, required: true },
|
|
95
|
-
// -- Timeline
|
|
96
|
-
timeline: { type: String },
|
|
97
100
|
// -- Rendering mode
|
|
98
101
|
mode: { type: String },
|
|
99
102
|
// -- Timing overrides
|
|
@@ -115,14 +118,14 @@ var PixodeskSvgAnimator = defineComponent({
|
|
|
115
118
|
time: { type: Number },
|
|
116
119
|
timeMs: { type: Number }
|
|
117
120
|
},
|
|
118
|
-
emits: ["play", "stop", "pause", "cancel", "finish", "remove"
|
|
119
|
-
setup(props, { expose }) {
|
|
121
|
+
emits: ["play", "stop", "pause", "cancel", "finish", "remove"],
|
|
122
|
+
setup(props, { expose, emit }) {
|
|
120
123
|
const elementRefs = /* @__PURE__ */ new Map();
|
|
121
124
|
const apiRef = shallowRef(null);
|
|
122
125
|
const compMode = computed(() => {
|
|
123
126
|
if (props.autoplay) return "autoplay" /* autoplay */;
|
|
124
127
|
if (props.time !== void 0 || props.timeMs !== void 0) return "fixedTime" /* fixedTime */;
|
|
125
|
-
if (props.play !== void 0) return "play" /* play */;
|
|
128
|
+
if (props.play !== void 0 || props.pause !== void 0) return "play" /* play */;
|
|
126
129
|
return "static" /* static */;
|
|
127
130
|
});
|
|
128
131
|
const resolvedDoc = computed(() => {
|
|
@@ -150,25 +153,59 @@ var PixodeskSvgAnimator = defineComponent({
|
|
|
150
153
|
destroyApi();
|
|
151
154
|
const doc = resolvedDoc.value;
|
|
152
155
|
if (!doc) return;
|
|
153
|
-
|
|
156
|
+
const callbacks = {
|
|
157
|
+
onPlay: () => emit("play"),
|
|
158
|
+
onPause: () => {
|
|
159
|
+
emit("pause");
|
|
160
|
+
emit("stop");
|
|
161
|
+
},
|
|
162
|
+
onCancel: () => {
|
|
163
|
+
emit("cancel");
|
|
164
|
+
emit("stop");
|
|
165
|
+
},
|
|
166
|
+
onFinish: () => {
|
|
167
|
+
emit("finish");
|
|
168
|
+
emit("stop");
|
|
169
|
+
},
|
|
170
|
+
onRemove: () => {
|
|
171
|
+
emit("remove");
|
|
172
|
+
emit("stop");
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
apiRef.value = createAnimator({ data: doc, adapter: createVueAdapter(elementRefs), callbacks });
|
|
176
|
+
syncPlayState();
|
|
177
|
+
applySeek();
|
|
154
178
|
}
|
|
155
179
|
function destroyApi() {
|
|
156
180
|
apiRef.value?.destroy();
|
|
157
181
|
apiRef.value = null;
|
|
158
182
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
183
|
+
function syncPlayState() {
|
|
184
|
+
if (compMode.value !== "play" /* play */) return;
|
|
185
|
+
if (props.play && !props.pause) {
|
|
186
|
+
apiRef.value?.play();
|
|
187
|
+
} else if (props.pause) {
|
|
188
|
+
apiRef.value?.pause();
|
|
189
|
+
} else if (props.play === false) {
|
|
190
|
+
apiRef.value?.finish();
|
|
191
|
+
} else {
|
|
192
|
+
apiRef.value?.play();
|
|
170
193
|
}
|
|
171
|
-
}
|
|
194
|
+
}
|
|
195
|
+
function applySeek() {
|
|
196
|
+
if (compMode.value !== "fixedTime" /* fixedTime */) return;
|
|
197
|
+
const doc = resolvedDoc.value;
|
|
198
|
+
if (!doc) return;
|
|
199
|
+
const seekMs = calcSeekMs(doc, props);
|
|
200
|
+
if (seekMs !== void 0) {
|
|
201
|
+
apiRef.value?.setCurrentTime(seekMs);
|
|
202
|
+
apiRef.value?.pause();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
onMounted(() => createApi());
|
|
206
|
+
watch(resolvedDoc, () => createApi(), { flush: "post" });
|
|
207
|
+
watch([compMode, () => props.play, () => props.pause], () => syncPlayState());
|
|
208
|
+
watch([compMode, () => props.time, () => props.timeMs], () => applySeek());
|
|
172
209
|
onUnmounted(() => {
|
|
173
210
|
destroyApi();
|
|
174
211
|
});
|
|
@@ -178,7 +215,8 @@ var PixodeskSvgAnimator = defineComponent({
|
|
|
178
215
|
pause: () => apiRef.value?.pause(),
|
|
179
216
|
cancel: () => apiRef.value?.cancel(),
|
|
180
217
|
finish: () => apiRef.value?.finish(),
|
|
181
|
-
|
|
218
|
+
setPlaybackRate: (rate) => apiRef.value?.setPlaybackRate(rate),
|
|
219
|
+
getCurrentTime: () => apiRef.value?.getCurrentTime() ?? null,
|
|
182
220
|
setCurrentTime: (time) => apiRef.value?.setCurrentTime(time)
|
|
183
221
|
};
|
|
184
222
|
expose(publicApi);
|