@pixodesk/svg-animator-vue 1.0.18 → 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 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 fractional position |
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", "warning", "error"],
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
- apiRef.value = (0, import_svg_animator_web.createAnimator)({ data: doc, adapter: createVueAdapter(elementRefs) });
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
- (0, import_vue.onMounted)(() => createApi());
178
- (0, import_vue.watch)(resolvedDoc, () => createApi());
179
- (0, import_vue.watch)([compMode, () => props.play, () => props.pause], () => {
180
- if (compMode.value === "play" /* play */) {
181
- if (props.play && !props.pause) {
182
- apiRef.value?.play();
183
- } else if (props.pause) {
184
- apiRef.value?.pause();
185
- } else {
186
- apiRef.value?.finish();
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
- getCurrentTime: () => apiRef.value?.getCurrentTime() || null,
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);
@@ -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" | "warning" | "error")[], "play" | "pause" | "stop" | "cancel" | "finish" | "remove" | "warning" | "error", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
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" | "warning" | "error")[], "play" | "pause" | "stop" | "cancel" | "finish" | "remove" | "warning" | "error", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
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", "warning", "error"],
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
- apiRef.value = createAnimator({ data: doc, adapter: createVueAdapter(elementRefs) });
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
- onMounted(() => createApi());
160
- watch(resolvedDoc, () => createApi());
161
- watch([compMode, () => props.play, () => props.pause], () => {
162
- if (compMode.value === "play" /* play */) {
163
- if (props.play && !props.pause) {
164
- apiRef.value?.play();
165
- } else if (props.pause) {
166
- apiRef.value?.pause();
167
- } else {
168
- apiRef.value?.finish();
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
- getCurrentTime: () => apiRef.value?.getCurrentTime() || null,
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);