@pixodesk/svg-animator-react 1.0.8 → 1.0.10

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/dist/index.umd.js CHANGED
@@ -1759,7 +1759,8 @@ var PixodeskAnimatorReact = (() => {
1759
1759
  // src/index.ts
1760
1760
  var index_exports = {};
1761
1761
  __export(index_exports, {
1762
- PixodeskSvgAnimator: () => PixodeskSvgAnimator_default
1762
+ PixodeskSvgAnimator: () => PixodeskSvgAnimator_default,
1763
+ PixodeskSvgCssAnimator: () => PixodeskSvgCssAnimator_default
1763
1764
  });
1764
1765
 
1765
1766
  // ../svg-animator-web/dist/index.js
@@ -1794,6 +1795,42 @@ var PixodeskAnimatorReact = (() => {
1794
1795
  }
1795
1796
  return target;
1796
1797
  };
1798
+ var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
1799
+ var PX_ANIM_ATTR_NAME = "_px_animator";
1800
+ var ANIMATE_ATTR = "animate";
1801
+ var TEXT_ATTR = "text";
1802
+ var TEXT_CONTENT_ATTR = "textContent";
1803
+ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
1804
+ "type",
1805
+ "children",
1806
+ ANIMATE_ATTR,
1807
+ "animator",
1808
+ "meta",
1809
+ "defs",
1810
+ "bindings",
1811
+ TEXT_ATTR,
1812
+ TEXT_CONTENT_ATTR
1813
+ ]);
1814
+ function isPxElementFileFormat(fileJson) {
1815
+ if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
1816
+ return false;
1817
+ }
1818
+ return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
1819
+ }
1820
+ function getAnimatorConfig(doc) {
1821
+ var _a, _b;
1822
+ return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator) || (doc == null ? void 0 : doc.animation) || ((_b = doc == null ? void 0 : doc.meta) == null ? void 0 : _b.animation);
1823
+ }
1824
+ function getDefs(doc) {
1825
+ var _a;
1826
+ if (!doc) return void 0;
1827
+ return doc.defs || ((_a = doc.meta) == null ? void 0 : _a.defs);
1828
+ }
1829
+ function getBindings(doc) {
1830
+ var _a;
1831
+ if (!doc) return void 0;
1832
+ return doc.bindings || ((_a = doc.meta) == null ? void 0 : _a.bindings);
1833
+ }
1797
1834
  function bezierToSvgPath(path) {
1798
1835
  var _a, _b, _c, _d;
1799
1836
  const v = path.v;
@@ -1882,50 +1919,104 @@ var PixodeskAnimatorReact = (() => {
1882
1919
  const t = (value - inMin) / (inMax - inMin);
1883
1920
  return outMin + t * (outMax - outMin);
1884
1921
  }
1885
- function cubicBezier(easing) {
1886
- const [p1x, p1y, p2x, p2y] = easing;
1922
+ function solveCubicBezierX(p1x, p2x, x) {
1923
+ if (x <= 0) return 0;
1924
+ if (x >= 1) return 1;
1887
1925
  const cx = 3 * p1x;
1888
1926
  const bx = 3 * (p2x - p1x) - cx;
1889
1927
  const ax = 1 - cx - bx;
1928
+ function sampleX(t) {
1929
+ return ((ax * t + bx) * t + cx) * t;
1930
+ }
1931
+ function sampleDX(t) {
1932
+ return (3 * ax * t + 2 * bx) * t + cx;
1933
+ }
1934
+ let t2 = x;
1935
+ let t0 = 0;
1936
+ let t1 = 1;
1937
+ for (let i = 0; i < 8; i++) {
1938
+ const x2 = sampleX(t2) - x;
1939
+ if (Math.abs(x2) < 1e-6) return t2;
1940
+ const d2 = sampleDX(t2);
1941
+ if (Math.abs(d2) < 1e-6) break;
1942
+ t2 -= x2 / d2;
1943
+ }
1944
+ t2 = x;
1945
+ while (t0 < t1) {
1946
+ const x2 = sampleX(t2);
1947
+ if (Math.abs(x2 - x) < 1e-6) return t2;
1948
+ if (x > x2) t0 = t2;
1949
+ else t1 = t2;
1950
+ t2 = (t1 + t0) / 2;
1951
+ }
1952
+ return t2;
1953
+ }
1954
+ function cubicBezier(easing) {
1955
+ const [p1x, p1y, p2x, p2y] = easing;
1890
1956
  const cy = 3 * p1y;
1891
1957
  const by = 3 * (p2y - p1y) - cy;
1892
1958
  const ay = 1 - cy - by;
1893
- function sampleCurveX(t) {
1894
- return ((ax * t + bx) * t + cx) * t;
1895
- }
1896
1959
  function sampleCurveY(t) {
1897
1960
  return ((ay * t + by) * t + cy) * t;
1898
1961
  }
1899
- function sampleCurveDerivativeX(t) {
1900
- return (3 * ax * t + 2 * bx) * t + cx;
1901
- }
1902
- function solveCurveX(x) {
1903
- if (x <= 0) return 0;
1904
- if (x >= 1) return 1;
1905
- let t2 = x;
1906
- let t0 = 0;
1907
- let t1 = 1;
1908
- for (let i = 0; i < 8; i++) {
1909
- const x2 = sampleCurveX(t2) - x;
1910
- if (Math.abs(x2) < 1e-6) return t2;
1911
- const d2 = sampleCurveDerivativeX(t2);
1912
- if (Math.abs(d2) < 1e-6) break;
1913
- t2 -= x2 / d2;
1914
- }
1915
- t2 = x;
1916
- while (t0 < t1) {
1917
- const x2 = sampleCurveX(t2);
1918
- if (Math.abs(x2 - x) < 1e-6) return t2;
1919
- if (x > x2) t0 = t2;
1920
- else t1 = t2;
1921
- t2 = (t1 + t0) / 2;
1922
- }
1923
- return t2;
1924
- }
1925
1962
  return function(x) {
1926
- return sampleCurveY(solveCurveX(x));
1963
+ return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
1927
1964
  };
1928
1965
  }
1966
+ function lerp2(a, b, t) {
1967
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
1968
+ }
1969
+ function subdivideCubicBezier(p0, p1, p2, p3, t) {
1970
+ const q0 = lerp2(p0, p1, t);
1971
+ const q1 = lerp2(p1, p2, t);
1972
+ const q2 = lerp2(p2, p3, t);
1973
+ const r0 = lerp2(q0, q1, t);
1974
+ const r1 = lerp2(q1, q2, t);
1975
+ const s = lerp2(r0, r1, t);
1976
+ return {
1977
+ left: [p0, q0, r0, s],
1978
+ right: [s, r1, q2, p3]
1979
+ };
1980
+ }
1981
+ function splitEasing(easing, xFraction) {
1982
+ if (!easing) return { left: void 0, right: void 0 };
1983
+ if (xFraction <= 0) return { left: void 0, right: easing };
1984
+ if (xFraction >= 1) return { left: easing, right: void 0 };
1985
+ const [x1, y1, x2, y2] = easing;
1986
+ const t = solveCubicBezierX(x1, x2, xFraction);
1987
+ const p0 = [0, 0];
1988
+ const p1 = [x1, y1];
1989
+ const p2 = [x2, y2];
1990
+ const p3 = [1, 1];
1991
+ const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
1992
+ const sx = left[3][0];
1993
+ const sy = left[3][1];
1994
+ let leftEasing;
1995
+ if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
1996
+ leftEasing = [
1997
+ left[1][0] / sx,
1998
+ left[1][1] / sy,
1999
+ left[2][0] / sx,
2000
+ left[2][1] / sy
2001
+ ];
2002
+ }
2003
+ let rightEasing;
2004
+ const rx = 1 - sx;
2005
+ const ry = 1 - sy;
2006
+ if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
2007
+ rightEasing = [
2008
+ (right[1][0] - sx) / rx,
2009
+ (right[1][1] - sy) / ry,
2010
+ (right[2][0] - sx) / rx,
2011
+ (right[2][1] - sy) / ry
2012
+ ];
2013
+ }
2014
+ return { left: leftEasing, right: rightEasing };
2015
+ }
2016
+ function reverseEasing(easing) {
2017
+ if (!easing) return void 0;
2018
+ return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
2019
+ }
1929
2020
  function toRGBA(color) {
1930
2021
  const r = Math.round(color[0] * 255);
1931
2022
  const g = Math.round(color[1] * 255);
@@ -2032,11 +2123,143 @@ var PixodeskAnimatorReact = (() => {
2032
2123
  return Math.max(min, Math.min(value, max));
2033
2124
  }
2034
2125
  var SVG_NS = "http://www.w3.org/2000/svg";
2035
- var INTERNAL_ATTRS = /* @__PURE__ */ new Set(["type", "children", "animate", "style", "animator", "defs", "bindings"]);
2036
- function createElement(tagName, props, style, children) {
2126
+ var ALLOWED_SVG_TAGS_LOWER_CASE = new Set([
2127
+ "svg",
2128
+ "g",
2129
+ "path",
2130
+ "circle",
2131
+ "ellipse",
2132
+ "rect",
2133
+ "line",
2134
+ "polyline",
2135
+ "polygon",
2136
+ "text",
2137
+ "tspan",
2138
+ "defs",
2139
+ "clipPath",
2140
+ "mask",
2141
+ "pattern",
2142
+ "linearGradient",
2143
+ "radialGradient",
2144
+ "stop",
2145
+ "use",
2146
+ "symbol",
2147
+ "marker",
2148
+ "filter",
2149
+ "feGaussianBlur",
2150
+ "feOffset",
2151
+ "feBlend",
2152
+ "feColorMatrix",
2153
+ "feMerge",
2154
+ "feMergeNode"
2155
+ ].map((tagName) => tagName.toLowerCase()));
2156
+ var ALLOWED_RESOURCE_ATTRIBUTES = [
2157
+ "href",
2158
+ // <use>
2159
+ "src",
2160
+ // <image>
2161
+ "filter",
2162
+ // url(#filterId)
2163
+ "clipPath",
2164
+ // clip-path="url(#clipPathId)"
2165
+ "mask",
2166
+ // url(#maskId)
2167
+ "markerStart",
2168
+ // marker-start="url(#markerId)"
2169
+ "markerMid",
2170
+ // marker-mid="url(#markerId)"
2171
+ "markerEnd"
2172
+ // marker-end="url(#markerId)"
2173
+ // 'fill', // url(#gradientId) or url(#patternId)
2174
+ // 'stroke', // url(#gradientId) or url(#patternId)
2175
+ // Don't allow 'cursor', can use external SVG, // url(cursor.svg)
2176
+ ];
2177
+ var ALLOWED_RESOURCE_ATTRIBUTES_SET = new Set(ALLOWED_RESOURCE_ATTRIBUTES);
2178
+ var ALLOWED_ATTRIBUTES_SET = /* @__PURE__ */ new Set([
2179
+ "href",
2180
+ "src",
2181
+ // Presentation
2182
+ "fill",
2183
+ "stroke",
2184
+ "strokeWidth",
2185
+ "opacity",
2186
+ "transform",
2187
+ // Geometry
2188
+ "x",
2189
+ "y",
2190
+ "cx",
2191
+ "cy",
2192
+ "r",
2193
+ "rx",
2194
+ "ry",
2195
+ "width",
2196
+ "height",
2197
+ "d",
2198
+ "x1",
2199
+ "y1",
2200
+ "x2",
2201
+ "y2",
2202
+ "points",
2203
+ // Text
2204
+ "fontSize",
2205
+ "fontFamily",
2206
+ "textAnchor",
2207
+ // Structure
2208
+ "id",
2209
+ "class",
2210
+ "viewBox",
2211
+ "preserveAspectRatio",
2212
+ // Gradient/Pattern
2213
+ "offset",
2214
+ "stopColor",
2215
+ "stopOpacity",
2216
+ "gradientTransform",
2217
+ // Clippath/Mask
2218
+ "clipPath",
2219
+ "mask",
2220
+ // Filter
2221
+ "filter",
2222
+ "stdDeviation",
2223
+ "in",
2224
+ "in2",
2225
+ "result",
2226
+ "mode",
2227
+ ...ALLOWED_RESOURCE_ATTRIBUTES
2228
+ ]);
2229
+ function sanitiseAttributeValue(name, value) {
2230
+ const nameLower = name.toLowerCase();
2231
+ if (!ALLOWED_ATTRIBUTES_SET.has(nameLower)) {
2232
+ console.warn("Attribute not in whitelist: ", nameLower);
2233
+ return void 0;
2234
+ }
2235
+ if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopColor") {
2236
+ const str = String(value);
2237
+ if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
2238
+ console.warn('Attribute "' + nameLower + '" blocked: url() references must be internal url(#id), got:', value);
2239
+ return void 0;
2240
+ }
2241
+ return value;
2242
+ }
2243
+ if (ALLOWED_RESOURCE_ATTRIBUTES_SET.has(nameLower)) {
2244
+ const str = String(value);
2245
+ if (str.startsWith("#")) {
2246
+ return value;
2247
+ }
2248
+ if (/^url\(#[^)]+\)$/.test(str)) {
2249
+ return value;
2250
+ }
2251
+ return void 0;
2252
+ }
2253
+ return value;
2254
+ }
2255
+ function createElement(tagName, normalisedProps, style, children, textContent) {
2256
+ if (!ALLOWED_SVG_TAGS_LOWER_CASE.has(tagName.toLowerCase())) return null;
2037
2257
  const element = document.createElementNS(SVG_NS, tagName);
2038
- for (const propName in props) {
2039
- element.setAttribute(camelCaseToKebabWordIfNeeded(propName), props[propName]);
2258
+ for (const propName in normalisedProps) {
2259
+ element.setAttribute(
2260
+ camelCaseToKebabWordIfNeeded(propName),
2261
+ sanitiseAttributeValue(propName, normalisedProps[propName])
2262
+ );
2040
2263
  }
2041
2264
  if (style) {
2042
2265
  for (const styleProp in style) {
@@ -2048,6 +2271,7 @@ var PixodeskAnimatorReact = (() => {
2048
2271
  element.appendChild(child);
2049
2272
  }
2050
2273
  }
2274
+ if (textContent) element.textContent = textContent;
2051
2275
  return element;
2052
2276
  }
2053
2277
  function resolveStyle(style, defs) {
@@ -2062,6 +2286,7 @@ var PixodeskAnimatorReact = (() => {
2062
2286
  const propsCopy = {};
2063
2287
  for (const key of Object.keys(props)) {
2064
2288
  if (INTERNAL_ATTRS.has(key)) continue;
2289
+ if (key === "style") continue;
2065
2290
  let value = props[key];
2066
2291
  if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
2067
2292
  propsCopy[key] = toRGBA(value);
@@ -2097,7 +2322,8 @@ var PixodeskAnimatorReact = (() => {
2097
2322
  type || "g",
2098
2323
  getNormalizedProps(props),
2099
2324
  resolvedStyle,
2100
- childElements
2325
+ childElements,
2326
+ props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
2101
2327
  );
2102
2328
  }
2103
2329
  function setupAnimationTriggers(api, config) {
@@ -2175,28 +2401,6 @@ var PixodeskAnimatorReact = (() => {
2175
2401
  }
2176
2402
  return api;
2177
2403
  }
2178
- var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
2179
- var PX_ANIM_ATTR_NAME = "_px_animator";
2180
- function isPxElementFileFormat(fileJson) {
2181
- if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
2182
- return false;
2183
- }
2184
- return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
2185
- }
2186
- function getAnimatorConfig(doc) {
2187
- var _a, _b;
2188
- return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator) || (doc == null ? void 0 : doc.animation) || ((_b = doc == null ? void 0 : doc.meta) == null ? void 0 : _b.animation);
2189
- }
2190
- function getDefs(doc) {
2191
- var _a;
2192
- if (!doc) return void 0;
2193
- return doc.defs || ((_a = doc.meta) == null ? void 0 : _a.defs);
2194
- }
2195
- function getBindings(doc) {
2196
- var _a;
2197
- if (!doc) return void 0;
2198
- return doc.bindings || ((_a = doc.meta) == null ? void 0 : _a.bindings);
2199
- }
2200
2404
  function parsePathCommands(d) {
2201
2405
  const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
2202
2406
  const commands = [];
@@ -2350,6 +2554,111 @@ var PixodeskAnimatorReact = (() => {
2350
2554
  }
2351
2555
  return results;
2352
2556
  }
2557
+ function interpolateValue(propName, a, b, t) {
2558
+ var _a, _b;
2559
+ if (propName === "d") {
2560
+ const aPaths = (_a = a == null ? void 0 : a.paths) != null ? _a : Array.isArray(a) ? a : [];
2561
+ const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
2562
+ return { paths: interpolateBeziers(aPaths, bPaths, t) };
2563
+ }
2564
+ if (COLOUR_ATTR_NAMES.has(propName)) {
2565
+ return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
2566
+ }
2567
+ if (TRANSFORM_FN_NAMES.has(propName) || propName === "stroke-dasharray" || propName === "strokeDasharray") {
2568
+ return interpolateVec(a || [], b || [], t);
2569
+ }
2570
+ return interpolateNum(+(a || 0), +(b || 0), t);
2571
+ }
2572
+ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2573
+ var _a, _b, _c, _d, _e;
2574
+ const totalIntervals = keyframes.length - 1;
2575
+ const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
2576
+ let segKfs;
2577
+ if (loop.before) {
2578
+ segKfs = keyframes.slice(0, segCount + 1);
2579
+ } else {
2580
+ segKfs = keyframes.slice(totalIntervals - segCount);
2581
+ }
2582
+ const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2583
+ const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2584
+ let fillStart, fillEnd;
2585
+ if (loop.before) {
2586
+ fillStart = 0;
2587
+ fillEnd = firstT;
2588
+ } else {
2589
+ fillStart = lastT;
2590
+ fillEnd = duration;
2591
+ }
2592
+ const fillDuration = fillEnd - fillStart;
2593
+ if (fillDuration <= 0) return keyframes;
2594
+ const segStartT = (_d = segKfs[0].t) != null ? _d : 0;
2595
+ const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
2596
+ const segDuration = segEndT - segStartT;
2597
+ if (segDuration <= 0) return keyframes;
2598
+ const template = segKfs.map((kf) => ({
2599
+ relT: (kf.t - segStartT) / segDuration,
2600
+ v: kf.v,
2601
+ e: kf.e
2602
+ }));
2603
+ const fullReps = Math.floor(fillDuration / segDuration);
2604
+ const remainder = fillDuration - fullReps * segDuration;
2605
+ const partialFraction = remainder / segDuration;
2606
+ const looped = [];
2607
+ function appendRep(repStart, isReversed, partial) {
2608
+ let entries;
2609
+ if (isReversed) {
2610
+ entries = [];
2611
+ for (let i = template.length - 1; i >= 0; i--) {
2612
+ entries.push({
2613
+ relT: 1 - template[i].relT,
2614
+ v: template[i].v,
2615
+ // Easing for reversed transition: use reversed easing from the forward "from" keyframe
2616
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0
2617
+ });
2618
+ }
2619
+ } else {
2620
+ entries = template;
2621
+ }
2622
+ const cutRelT = partial !== void 0 ? partial : 1;
2623
+ for (let i = 0; i < entries.length; i++) {
2624
+ const entry = entries[i];
2625
+ if (entry.relT > cutRelT + 1e-9) {
2626
+ const prev = entries[i - 1];
2627
+ const intervalSpan = entry.relT - prev.relT;
2628
+ const localFrac = (cutRelT - prev.relT) / intervalSpan;
2629
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
2630
+ const cutValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
2631
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
2632
+ if (looped.length > 0 && prev.relT <= cutRelT) {
2633
+ looped[looped.length - 1].e = leftEasing;
2634
+ }
2635
+ looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2636
+ return;
2637
+ }
2638
+ looped.push({
2639
+ t: repStart + entry.relT * segDuration,
2640
+ v: entry.v,
2641
+ e: i < entries.length - 1 ? entry.e : void 0
2642
+ });
2643
+ }
2644
+ }
2645
+ for (let rep = 0; rep < fullReps; rep++) {
2646
+ const distFromBoundary = loop.before ? fullReps - 1 - rep : rep;
2647
+ const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
2648
+ const repStart = fillStart + rep * segDuration;
2649
+ appendRep(repStart, isReversed);
2650
+ }
2651
+ if (partialFraction > 1e-9) {
2652
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
2653
+ const repStart = fillStart + fullReps * segDuration;
2654
+ appendRep(repStart, isReversed, partialFraction);
2655
+ }
2656
+ if (loop.before) {
2657
+ return [...looped, ...keyframes];
2658
+ } else {
2659
+ return [...keyframes, ...looped];
2660
+ }
2661
+ }
2353
2662
  function normalizeKeyframes(propName, propAnim, duration, defs) {
2354
2663
  var _a, _b, _c, _d, _e;
2355
2664
  const keyframes = propAnim.keyframes || propAnim.kfs || [];
@@ -2374,6 +2683,11 @@ var PixodeskAnimatorReact = (() => {
2374
2683
  var _a2, _b2;
2375
2684
  return ((_a2 = a.t) != null ? _a2 : 0) - ((_b2 = b.t) != null ? _b2 : 0);
2376
2685
  });
2686
+ const loopRaw = propAnim.loop;
2687
+ const loop = loopRaw === true ? {} : loopRaw || void 0;
2688
+ if (loop && normalized.length >= 2) {
2689
+ return expandLoopKeyframes(propName, normalized, loop, duration);
2690
+ }
2377
2691
  return normalized;
2378
2692
  }
2379
2693
  function mergeAnimationDefinitions(animations) {
@@ -2804,40 +3118,56 @@ var PixodeskAnimatorReact = (() => {
2804
3118
  };
2805
3119
  return adapter;
2806
3120
  }
3121
+ function createCssKf(kf, t, propName, unsupportedSet) {
3122
+ var _a, _b;
3123
+ let value = (_a = kf.v) != null ? _a : kf.value;
3124
+ const e = (_b = kf.e) != null ? _b : kf.easing;
3125
+ const cssKf = {
3126
+ offset: t,
3127
+ easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
3128
+ };
3129
+ let cssValue;
3130
+ let cssKey = propName;
3131
+ if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
3132
+ cssValue = toRGBA(value);
3133
+ } else if (TRANSFORM_FN_NAMES.has(propName)) {
3134
+ if (Array.isArray(value)) {
3135
+ if (propName === "translate") value = value.map((v) => v + "px");
3136
+ value = value.join(",");
3137
+ }
3138
+ if (propName === "rotate") value = value + "deg";
3139
+ cssValue = propName + "(" + value + ")";
3140
+ cssKey = "transform";
3141
+ } else {
3142
+ cssValue = "" + value;
3143
+ }
3144
+ if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
3145
+ cssKey = kebabToCamelCaseWord(cssKey);
3146
+ cssKf[cssKey] = cssValue;
3147
+ return cssKf;
3148
+ }
2807
3149
  function convertToWebApiKeyframes(animDef, unsupportedSet, config) {
2808
- var _a, _b, _c, _d;
3150
+ var _a, _b;
2809
3151
  const result = /* @__PURE__ */ new Map();
2810
3152
  for (const [propName, propAnim] of Object.entries(animDef)) {
2811
3153
  const keyframes = propAnim.kfs || propAnim.keyframes || [];
2812
3154
  const cssKeyframes = [];
2813
- for (const kf of keyframes) {
3155
+ for (let i = 0; i < keyframes.length; i++) {
3156
+ const kf = keyframes[i];
2814
3157
  let t = (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
2815
3158
  t = clamp(t / (config.duration || 1), 0, 1);
2816
- let value = (_c = kf.v) != null ? _c : kf.value;
2817
- const e = (_d = kf.e) != null ? _d : kf.easing;
2818
- const cssKf = {
2819
- offset: t,
2820
- easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
2821
- };
2822
- let cssValue;
2823
- let cssKey = propName;
2824
- if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
2825
- cssValue = toRGBA(value);
2826
- } else if (TRANSFORM_FN_NAMES.has(propName)) {
2827
- if (Array.isArray(value)) {
2828
- if (propName === "translate") value = value.map((v) => v + "px");
2829
- value = value.join(",");
2830
- }
2831
- if (propName === "rotate") value = value + "deg";
2832
- cssValue = propName + "(" + value + ")";
2833
- cssKey = "transform";
2834
- } else {
2835
- cssValue = "" + value;
3159
+ const cssKf = createCssKf(kf, t, propName, unsupportedSet);
3160
+ if (i === 0 && (cssKf.offset || 0) > 0) {
3161
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), {
3162
+ offset: 0
3163
+ }));
2836
3164
  }
2837
- if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
2838
- cssKey = kebabToCamelCaseWord(cssKey);
2839
- cssKf[cssKey] = cssValue;
2840
3165
  cssKeyframes.push(cssKf);
3166
+ if (i === keyframes.length - 1 && (cssKf.offset || 0) < 1) {
3167
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), {
3168
+ offset: 1
3169
+ }));
3170
+ }
2841
3171
  }
2842
3172
  if (cssKeyframes.length > 0) {
2843
3173
  result.set(propName, cssKeyframes);
@@ -2878,17 +3208,17 @@ var PixodeskAnimatorReact = (() => {
2878
3208
  console.warn('createWebApiAnimator: No elements found for selector "' + selector + '"');
2879
3209
  }
2880
3210
  const keyframesMap = convertToWebApiKeyframes(animDef, unsupportedSet, config);
3211
+ const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
3212
+ const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
3213
+ const effectOptions = {
3214
+ duration: config.duration,
3215
+ delay: positiveDelay,
3216
+ fill: config.fill,
3217
+ direction: config.direction,
3218
+ iterations
3219
+ };
2881
3220
  for (let i = 0; i < elements.length; i++) {
2882
3221
  const element = elements[i];
2883
- const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
2884
- const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
2885
- const effectOptions = {
2886
- duration: config.duration,
2887
- delay: positiveDelay,
2888
- fill: config.fill,
2889
- direction: config.direction,
2890
- iterations
2891
- };
2892
3222
  for (const [, keyframes] of keyframesMap) {
2893
3223
  if (keyframes.length > 0) {
2894
3224
  try {
@@ -2940,7 +3270,20 @@ var PixodeskAnimatorReact = (() => {
2940
3270
  (_a = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a.call(callbacks);
2941
3271
  },
2942
3272
  "finish": () => {
2943
- animations.forEach((a) => a.finish());
3273
+ var _a;
3274
+ for (const a of animations) {
3275
+ try {
3276
+ if (((_a = a.effect) == null ? void 0 : _a.getTiming().iterations) === Infinity) {
3277
+ a.effect.updateTiming({ iterations: 1 });
3278
+ a.finish();
3279
+ a.effect.updateTiming({ iterations: Infinity });
3280
+ } else {
3281
+ a.finish();
3282
+ }
3283
+ } catch (e) {
3284
+ a.cancel();
3285
+ }
3286
+ }
2944
3287
  },
2945
3288
  "setPlaybackRate": (rate) => {
2946
3289
  animations.forEach((a) => a.playbackRate = rate);
@@ -3069,7 +3412,7 @@ var PixodeskAnimatorReact = (() => {
3069
3412
  value[styleProp] = replaceUrlRefs(styleValue, idMap);
3070
3413
  }
3071
3414
  }
3072
- } else if (typeof value === "object" && value !== null && key !== "animate") {
3415
+ } else if (typeof value === "object" && value !== null && key !== ANIMATE_ATTR) {
3073
3416
  updateRefs(value);
3074
3417
  }
3075
3418
  }
@@ -3431,6 +3774,41 @@ var PixodeskAnimatorReact = (() => {
3431
3774
  );
3432
3775
  };
3433
3776
  var PixodeskSvgAnimator_default = PixodeskSvgAnimator;
3777
+
3778
+ // src/PixodeskSvgCssAnimator.tsx
3779
+ var import_react3 = __toESM(require_react(), 1);
3780
+ var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
3781
+ var PixodeskSvgCssAnimator = ({ className, style, children, startOn = "load", outAction = "continue" }) => {
3782
+ const [state, setState] = (0, import_react3.useState)(startOn === "load" ? "playing" : "idle");
3783
+ const ref = (0, import_react3.useRef)(null);
3784
+ (0, import_react3.useEffect)(() => {
3785
+ if (startOn !== "scrollIntoView") return;
3786
+ const el = ref.current;
3787
+ if (!el) return;
3788
+ const outState = outAction === "reset" ? "idle" : outAction === "pause" ? "paused" : "playing";
3789
+ const observer = new IntersectionObserver(
3790
+ ([entry]) => setState(entry.isIntersecting ? "playing" : outState),
3791
+ { threshold: 0.1 }
3792
+ );
3793
+ observer.observe(el);
3794
+ return () => observer.disconnect();
3795
+ }, [startOn, outAction]);
3796
+ const goOut = () => setState(
3797
+ outAction === "reset" ? "idle" : outAction === "pause" ? "paused" : "playing"
3798
+ );
3799
+ const handlers = startOn === "mouseOver" ? { onMouseEnter: () => setState("playing"), onMouseLeave: goOut } : startOn === "click" ? { onClick: () => state === "playing" ? goOut() : setState("playing") } : {};
3800
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
3801
+ "div",
3802
+ {
3803
+ ref,
3804
+ className: className + (state === "playing" ? "px-anim-enabled px-anim-playing" : state === "paused" ? "px-anim-enabled" : ""),
3805
+ style,
3806
+ ...handlers,
3807
+ children
3808
+ }
3809
+ );
3810
+ };
3811
+ var PixodeskSvgCssAnimator_default = PixodeskSvgCssAnimator;
3434
3812
  return __toCommonJS(index_exports);
3435
3813
  })();
3436
3814
  /*! Bundled license information: