@pixodesk/svg-animator-web 1.0.26 → 1.0.27

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
@@ -63,111 +63,583 @@ var PixodeskAnimator = (() => {
63
63
  setupAnimationTriggers: () => setupAnimationTriggers
64
64
  });
65
65
 
66
- // ../svg-animator-core/src/PxSchema.ts
67
- function pathStr(path) {
68
- if (!path.length) return ".";
69
- let result = "";
70
- for (const seg of path) {
71
- if (seg.startsWith("[")) result += seg;
72
- else result += (result ? "." : "") + seg;
66
+ // ../svg-animator-core/src/PxAnimatorUtil.ts
67
+ function bezierToSvgPath(path, forceCurves = false) {
68
+ var _a, _b, _c, _d;
69
+ const v = path.v;
70
+ const i = path.i;
71
+ const o = path.o;
72
+ const c = path.c;
73
+ if (!v.length) return "";
74
+ const d = [];
75
+ const len = v.length;
76
+ d.push("M" + v[0][0] + "," + v[0][1]);
77
+ for (let idx = 1; idx < len; idx++) {
78
+ const prevV = v[idx - 1];
79
+ const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
80
+ const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
81
+ const currV = v[idx];
82
+ const isLine = !forceCurves && (prevO[0] === prevV[0] && prevO[1] === prevV[1]) && (currI[0] === currV[0] && currI[1] === currV[1]);
83
+ if (isLine) {
84
+ d.push("L" + currV[0] + "," + currV[1]);
85
+ } else {
86
+ d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
87
+ }
73
88
  }
74
- return result;
75
- }
76
- var Base = class {
77
- _canSanitize(raw) {
78
- return this.isValid(raw);
89
+ if (c && len > 0) {
90
+ const lastV = v[len - 1];
91
+ const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
92
+ const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
93
+ const firstV = v[0];
94
+ const isLine = !forceCurves && (lastO[0] === lastV[0] && lastO[1] === lastV[1]) && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
95
+ if (!isLine) {
96
+ d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
97
+ }
98
+ d.push("z");
79
99
  }
80
- optional() {
81
- return new Optional(this);
100
+ return d.join("");
101
+ }
102
+ function interpolateNum(a, b, t) {
103
+ return a + (b - a) * t;
104
+ }
105
+ function interpolateVec(a, b, t) {
106
+ const res = [];
107
+ const count = Math.max(a.length, b.length);
108
+ for (let i = 0; i < count; i++) {
109
+ res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
82
110
  }
83
- };
84
- var Optional = class extends Base {
85
- constructor(inner) {
86
- super();
87
- this.inner = inner;
88
- this._default = void 0;
111
+ return res;
112
+ }
113
+ function interpolateColor(a, b, t) {
114
+ return [
115
+ interpolateNum(a[0] || 0, b[0] || 0, t),
116
+ interpolateNum(a[1] || 0, b[1] || 0, t),
117
+ interpolateNum(a[2] || 0, b[2] || 0, t),
118
+ interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
119
+ ];
120
+ }
121
+ function interpolateBeziers(paths1, paths2, progress) {
122
+ const count = Math.max(paths1.length, paths2.length);
123
+ const res = [];
124
+ for (let i = 0; i < count; i++) {
125
+ res.push(interpolateBezier(paths1[i], paths2[i], progress));
89
126
  }
90
- sanitize(raw) {
91
- if (raw === void 0 || raw === null) return void 0;
92
- return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
127
+ return res;
128
+ }
129
+ function interpolateBezier(path1, path2, progress) {
130
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
131
+ if (!path1 || !path2) return path1 || path2 || { v: [] };
132
+ const t = Math.min(Math.max(progress, 0), 1);
133
+ const len = Math.min(path1.v.length, path2.v.length);
134
+ const v = [];
135
+ const i = [];
136
+ const o = [];
137
+ for (let idx = 0; idx < len; idx++) {
138
+ const v1 = path1.v[idx];
139
+ const v2 = path2.v[idx];
140
+ v.push(interpolateVec(v1, v2, t));
141
+ const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
142
+ const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
143
+ i.push(interpolateVec(i1, i2, t));
144
+ const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
145
+ const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
146
+ o.push(interpolateVec(o1, o2, t));
93
147
  }
94
- isValid(raw, ctx, path) {
95
- if (raw === void 0 || raw === null) return true;
96
- return this.inner.isValid(raw, ctx, path);
148
+ return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
149
+ }
150
+ function remap(value, inMin, inMax, outMin, outMax) {
151
+ if (inMax === inMin) return outMin;
152
+ const t = (value - inMin) / (inMax - inMin);
153
+ return outMin + t * (outMax - outMin);
154
+ }
155
+ function solveCubicBezierX(p1x, p2x, x) {
156
+ if (x <= 0) return 0;
157
+ if (x >= 1) return 1;
158
+ const cx = 3 * p1x;
159
+ const bx = 3 * (p2x - p1x) - cx;
160
+ const ax = 1 - cx - bx;
161
+ function sampleX(t) {
162
+ return ((ax * t + bx) * t + cx) * t;
97
163
  }
98
- _canSanitize(raw) {
99
- return raw === void 0 || raw === null || this.inner._canSanitize(raw);
164
+ function sampleDX(t) {
165
+ return (3 * ax * t + 2 * bx) * t + cx;
100
166
  }
101
- };
102
- var Str = class extends Base {
103
- constructor(_default = "") {
104
- super();
105
- this._default = _default;
167
+ let t2 = x;
168
+ let t0 = 0;
169
+ let t1 = 1;
170
+ for (let i = 0; i < 8; i++) {
171
+ const x2 = sampleX(t2) - x;
172
+ if (Math.abs(x2) < 1e-6) return t2;
173
+ const d2 = sampleDX(t2);
174
+ if (Math.abs(d2) < 1e-6) break;
175
+ t2 -= x2 / d2;
106
176
  }
107
- sanitize(raw) {
108
- return typeof raw === "string" ? raw : this._default;
177
+ t2 = x;
178
+ while (t0 < t1) {
179
+ const x2 = sampleX(t2);
180
+ if (Math.abs(x2 - x) < 1e-6) return t2;
181
+ if (x > x2) t0 = t2;
182
+ else t1 = t2;
183
+ t2 = (t1 + t0) / 2;
109
184
  }
110
- isValid(raw, ctx, path) {
111
- if (typeof raw === "string") return true;
112
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected string, got " + typeof raw);
113
- return false;
185
+ return t2;
186
+ }
187
+ function cubicBezier(easing) {
188
+ const [p1x, p1y, p2x, p2y] = easing;
189
+ const cy = 3 * p1y;
190
+ const by = 3 * (p2y - p1y) - cy;
191
+ const ay = 1 - cy - by;
192
+ function sampleCurveY(t) {
193
+ return ((ay * t + by) * t + cy) * t;
114
194
  }
115
- };
116
- var Num = class extends Base {
117
- constructor(_default = 0) {
118
- super();
119
- this._default = _default;
195
+ return function(x) {
196
+ return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
197
+ };
198
+ }
199
+ function lerp2(a, b, t) {
200
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
201
+ }
202
+ function subdivideCubicBezier(p0, p1, p2, p3, t) {
203
+ const q0 = lerp2(p0, p1, t);
204
+ const q1 = lerp2(p1, p2, t);
205
+ const q2 = lerp2(p2, p3, t);
206
+ const r0 = lerp2(q0, q1, t);
207
+ const r1 = lerp2(q1, q2, t);
208
+ const s = lerp2(r0, r1, t);
209
+ return {
210
+ left: [p0, q0, r0, s],
211
+ right: [s, r1, q2, p3]
212
+ };
213
+ }
214
+ function splitEasing(easing, xFraction) {
215
+ if (!easing) return { left: void 0, right: void 0 };
216
+ if (xFraction <= 0) return { left: void 0, right: easing };
217
+ if (xFraction >= 1) return { left: easing, right: void 0 };
218
+ const [x1, y1, x2, y2] = easing;
219
+ const t = solveCubicBezierX(x1, x2, xFraction);
220
+ const p0 = [0, 0];
221
+ const p1 = [x1, y1];
222
+ const p2 = [x2, y2];
223
+ const p3 = [1, 1];
224
+ const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
225
+ const sx = left[3][0];
226
+ const sy = left[3][1];
227
+ let leftEasing;
228
+ if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
229
+ leftEasing = [
230
+ left[1][0] / sx,
231
+ left[1][1] / sy,
232
+ left[2][0] / sx,
233
+ left[2][1] / sy
234
+ ];
120
235
  }
121
- sanitize(raw) {
122
- return typeof raw === "number" && isFinite(raw) ? raw : this._default;
236
+ let rightEasing;
237
+ const rx = 1 - sx;
238
+ const ry = 1 - sy;
239
+ if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
240
+ rightEasing = [
241
+ (right[1][0] - sx) / rx,
242
+ (right[1][1] - sy) / ry,
243
+ (right[2][0] - sx) / rx,
244
+ (right[2][1] - sy) / ry
245
+ ];
123
246
  }
124
- isValid(raw, ctx, path) {
125
- if (typeof raw === "number" && isFinite(raw)) return true;
126
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected finite number, got " + JSON.stringify(raw));
127
- return false;
247
+ return { left: leftEasing, right: rightEasing };
248
+ }
249
+ function reverseEasing(easing) {
250
+ if (!easing) return void 0;
251
+ return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
252
+ }
253
+ function toRGBA(color) {
254
+ const r = Math.round(color[0] * 255);
255
+ const g = Math.round(color[1] * 255);
256
+ const b = Math.round(color[2] * 255);
257
+ return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
258
+ }
259
+ function parseRgba(s) {
260
+ var _a;
261
+ const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
262
+ if (!inner) throw new Error("Invalid rgb/rgba format");
263
+ const parts = inner.split(",").map((v) => +v.trim());
264
+ return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
265
+ }
266
+ function parseHex(s) {
267
+ const hex = s.slice(1);
268
+ const isShort = hex.length <= 4;
269
+ const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
270
+ const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
271
+ const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
272
+ const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
273
+ const result = [
274
+ parseInt(r, 16) / 255,
275
+ parseInt(g, 16) / 255,
276
+ parseInt(b, 16) / 255
277
+ ];
278
+ if (a !== null) {
279
+ result.push(parseInt(a, 16) / 255);
128
280
  }
129
- };
130
- var Bool = class extends Base {
131
- constructor(_default = false) {
132
- super();
133
- this._default = _default;
281
+ return result;
282
+ }
283
+ function parseColor(s) {
284
+ if (!s) return void 0;
285
+ if (Array.isArray(s)) return s;
286
+ if (typeof s !== "string") return void 0;
287
+ if (s.startsWith("#")) {
288
+ return parseHex(s);
289
+ } else if (s.startsWith("rgb")) {
290
+ return parseRgba(s);
291
+ } else {
292
+ console.warn("Unsupported color format: " + s);
134
293
  }
135
- sanitize(raw) {
136
- return typeof raw === "boolean" ? raw : this._default;
294
+ return void 0;
295
+ }
296
+ var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
297
+ var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
298
+ var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
299
+ function composeTransformParts(parts, opts) {
300
+ var _a;
301
+ if (!parts) return "";
302
+ const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
303
+ const segs = [];
304
+ const t = parts.translate;
305
+ const o = parts.origin;
306
+ const r = parts.rotate;
307
+ const k = parts.skew;
308
+ const s = parts.scale;
309
+ const tu = withUnits ? "px" : "";
310
+ const ru = withUnits ? "deg" : "";
311
+ if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
312
+ if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
313
+ if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
314
+ if (k !== void 0 && k !== null) segs.push("skewX(" + k + ru + ")");
315
+ if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
316
+ if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
317
+ return segs.join("");
318
+ }
319
+ var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
320
+ var DEFAULT_DURATION_MS = 1e3;
321
+ function kebabToCamelCaseWord(kebab) {
322
+ return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
323
+ }
324
+ function isCamelCaseWord(word) {
325
+ return !word.includes("-") && /[a-z][A-Z]/.test(word);
326
+ }
327
+ var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
328
+ // Transform/positioning
329
+ "viewBox",
330
+ "preserveAspectRatio",
331
+ // Gradient
332
+ "gradientUnits",
333
+ "gradientTransform",
334
+ "spreadMethod",
335
+ // Pattern
336
+ "patternUnits",
337
+ "patternContentUnits",
338
+ "patternTransform",
339
+ // Clipping/masking
340
+ "clipPathUnits",
341
+ "maskUnits",
342
+ "maskContentUnits",
343
+ // Marker (SVG spec keeps these camelCase, like viewBox)
344
+ "markerUnits",
345
+ "markerWidth",
346
+ "markerHeight",
347
+ "refX",
348
+ "refY",
349
+ // Text
350
+ "textLength",
351
+ "lengthAdjust",
352
+ "startOffset",
353
+ // Filter
354
+ "filterUnits",
355
+ "primitiveUnits",
356
+ "tableValues",
357
+ // feFuncR/G/B/A transfer table (type="table")
358
+ "stdDeviation",
359
+ "baseFrequency",
360
+ "numOctaves",
361
+ "surfaceScale",
362
+ "diffuseConstant",
363
+ "specularConstant",
364
+ "specularExponent",
365
+ "kernelMatrix",
366
+ "kernelUnitLength",
367
+ "edgeMode",
368
+ "preserveAlpha",
369
+ "targetX",
370
+ "targetY"
371
+ // // Animation
372
+ // 'attributeName',
373
+ // 'attributeType',
374
+ // 'calcMode',
375
+ // 'keyTimes',
376
+ // 'keySplines',
377
+ // 'repeatCount',
378
+ // 'repeatDur'
379
+ ]);
380
+ function camelCaseToKebabWordIfNeeded(camel) {
381
+ return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
382
+ }
383
+ function clamp(value, min, max) {
384
+ return Math.max(min, Math.min(value, max));
385
+ }
386
+ function bezier2D_pointAt(P0, P1, P2, P3, t) {
387
+ if (t <= 0) return [P0[0], P0[1]];
388
+ if (t >= 1) return [P3[0], P3[1]];
389
+ const u = 1 - t;
390
+ const u2 = u * u;
391
+ const u3 = u2 * u;
392
+ const t2 = t * t;
393
+ const t3 = t2 * t;
394
+ const w0 = u3;
395
+ const w1 = 3 * t * u2;
396
+ const w2 = 3 * t2 * u;
397
+ const w3 = t3;
398
+ return [
399
+ w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
400
+ w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
401
+ ];
402
+ }
403
+ var BEZIER_T_NUDGE = 1e-4;
404
+ function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
405
+ const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
406
+ if (result[0] === 0 && result[1] === 0) {
407
+ const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
408
+ return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
137
409
  }
138
- isValid(raw, ctx, path) {
139
- if (typeof raw === "boolean") return true;
140
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected boolean, got " + typeof raw);
141
- return false;
410
+ return result;
411
+ }
412
+ function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
413
+ const u = 1 - t;
414
+ const a = 3 * u * u;
415
+ const b = 6 * t * u;
416
+ const c = 3 * t * t;
417
+ return [
418
+ a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
419
+ a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
420
+ ];
421
+ }
422
+ function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
423
+ const n = steps + 1;
424
+ const ts = new Float64Array(n);
425
+ const ds = new Float64Array(n);
426
+ let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
427
+ ts[0] = 0;
428
+ ds[0] = 0;
429
+ let cum = 0;
430
+ for (let i = 1; i < n; i++) {
431
+ const t = i / steps;
432
+ const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
433
+ const dx = cur[0] - prev[0];
434
+ const dy = cur[1] - prev[1];
435
+ cum += Math.sqrt(dx * dx + dy * dy);
436
+ ts[i] = t;
437
+ ds[i] = cum;
438
+ prev = cur;
439
+ }
440
+ return { ts, ds };
441
+ }
442
+ function bezier2D_tForDistance(lut, distance) {
443
+ const { ts, ds } = lut;
444
+ const last = ds.length - 1;
445
+ if (distance <= 0) return ts[0];
446
+ if (distance >= ds[last]) return ts[last];
447
+ let lo = 1;
448
+ let hi = last;
449
+ while (lo < hi) {
450
+ const mid = lo + hi >>> 1;
451
+ if (ds[mid] < distance) lo = mid + 1;
452
+ else hi = mid;
453
+ }
454
+ const dPrev = ds[hi - 1];
455
+ const dCur = ds[hi];
456
+ const span = dCur - dPrev;
457
+ const frac = span > 0 ? (distance - dPrev) / span : 0;
458
+ return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
459
+ }
460
+ function bezier2D_arcAtT(lut, t) {
461
+ const { ts, ds } = lut;
462
+ const last = ts.length - 1;
463
+ if (t <= ts[0]) return ds[0];
464
+ if (t >= ts[last]) return ds[last];
465
+ let lo = 1, hi = last;
466
+ while (lo < hi) {
467
+ const mid = lo + hi >>> 1;
468
+ if (ts[mid] < t) lo = mid + 1;
469
+ else hi = mid;
470
+ }
471
+ const tPrev = ts[hi - 1];
472
+ const span = ts[hi] - tPrev;
473
+ const frac = span > 0 ? (t - tPrev) / span : 0;
474
+ return ds[hi - 1] + frac * (ds[hi] - ds[hi - 1]);
475
+ }
476
+ function invertEasing(easing) {
477
+ if (!easing) return (y) => y;
478
+ const flipped = [easing[1], easing[0], easing[3], easing[2]];
479
+ return cubicBezier(flipped);
480
+ }
481
+
482
+ // ../svg-animator-core/src/PxScrollMath.ts
483
+ function isScrollTimeline(config) {
484
+ return (config == null ? void 0 : config.timelineSource) === "scroll";
485
+ }
486
+ function scrollTotalDurationMs(config) {
487
+ const duration = typeof (config == null ? void 0 : config.duration) === "number" && config.duration > 0 ? config.duration : DEFAULT_DURATION_MS;
488
+ const iterations = typeof (config == null ? void 0 : config.iterations) === "number" && config.iterations > 0 ? config.iterations : 1;
489
+ return duration * iterations;
490
+ }
491
+ function scrollPhaseInterval(phase, subjectSize, scrollportSize) {
492
+ const s = subjectSize, vp = scrollportSize;
493
+ switch (phase) {
494
+ case "cover":
495
+ return [0, s + vp];
496
+ case "entry":
497
+ return [0, Math.min(s, vp)];
498
+ case "contain":
499
+ return [Math.min(s, vp), Math.max(s, vp)];
500
+ case "exit":
501
+ return [Math.max(s, vp), s + vp];
502
+ case "entry-crossing":
503
+ return [0, s];
504
+ case "exit-crossing":
505
+ return [vp, s + vp];
506
+ }
507
+ }
508
+ var DEFAULT_PHASE = "cover";
509
+ function resolveRangePointU(point, defaultFraction, subjectSize, scrollportSize) {
510
+ var _a;
511
+ const [u0, u1] = scrollPhaseInterval((_a = point == null ? void 0 : point.phase) != null ? _a : DEFAULT_PHASE, subjectSize, scrollportSize);
512
+ const fraction = typeof (point == null ? void 0 : point.fraction) === "number" ? point.fraction : defaultFraction;
513
+ return u0 + fraction * (u1 - u0);
514
+ }
515
+ function scrollViewProgress(subjectStart, subjectSize, scrollportSize, range) {
516
+ const u = scrollportSize - subjectStart;
517
+ const uStart = resolveRangePointU(range == null ? void 0 : range.start, 0, subjectSize, scrollportSize);
518
+ const uEnd = resolveRangePointU(range == null ? void 0 : range.end, 1, subjectSize, scrollportSize);
519
+ if (uEnd <= uStart) return u >= uEnd ? 1 : 0;
520
+ return clamp((u - uStart) / (uEnd - uStart), 0, 1);
521
+ }
522
+ function scrollOffsetProgress(offset, maxOffset, range) {
523
+ var _a, _b;
524
+ const raw = maxOffset > 0 ? clamp(offset / maxOffset, 0, 1) : 1;
525
+ const start = typeof ((_a = range == null ? void 0 : range.start) == null ? void 0 : _a.fraction) === "number" ? range.start.fraction : 0;
526
+ const end = typeof ((_b = range == null ? void 0 : range.end) == null ? void 0 : _b.fraction) === "number" ? range.end.fraction : 1;
527
+ if (end <= start) return raw >= end ? 1 : 0;
528
+ return clamp((raw - start) / (end - start), 0, 1);
529
+ }
530
+ function scrollResolveAxis(axis, writingMode) {
531
+ const a = axis != null ? axis : "block";
532
+ if (a === "x" || a === "y") return a;
533
+ const vertical = !!writingMode && writingMode.startsWith("vertical");
534
+ if (a === "inline") return vertical ? "y" : "x";
535
+ return vertical ? "x" : "y";
536
+ }
537
+
538
+ // ../svg-animator-core/src/PxSchema.ts
539
+ function pathStr(path) {
540
+ if (!path.length) return ".";
541
+ let result = "";
542
+ for (const seg of path) {
543
+ if (seg.startsWith("[")) result += seg;
544
+ else result += (result ? "." : "") + seg;
545
+ }
546
+ return result;
547
+ }
548
+ var Base = class {
549
+ _canSanitize(raw) {
550
+ return this.isValid(raw);
551
+ }
552
+ optional() {
553
+ return new Optional(this);
142
554
  }
143
555
  };
144
- var Literal = class extends Base {
145
- constructor(value) {
556
+ var Optional = class extends Base {
557
+ constructor(inner) {
146
558
  super();
147
- this.value = value;
148
- this._default = value;
559
+ this.inner = inner;
560
+ this._default = void 0;
149
561
  }
150
562
  sanitize(raw) {
151
- return raw === this.value ? this.value : this._default;
563
+ if (raw === void 0 || raw === null) return void 0;
564
+ return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
152
565
  }
153
566
  isValid(raw, ctx, path) {
154
- if (raw === this.value) return true;
155
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected " + JSON.stringify(this.value) + ", got " + JSON.stringify(raw));
156
- return false;
567
+ if (raw === void 0 || raw === null) return true;
568
+ return this.inner.isValid(raw, ctx, path);
569
+ }
570
+ _canSanitize(raw) {
571
+ return raw === void 0 || raw === null || this.inner._canSanitize(raw);
157
572
  }
158
573
  };
159
- var Enum = class extends Base {
160
- constructor(values, defaultVal) {
574
+ var Str = class extends Base {
575
+ constructor(_default = "") {
161
576
  super();
162
- this.values = values;
163
- this._default = defaultVal != null ? defaultVal : values[0];
577
+ this._default = _default;
164
578
  }
165
579
  sanitize(raw) {
166
- return this.values.includes(raw) ? raw : this._default;
580
+ return typeof raw === "string" ? raw : this._default;
167
581
  }
168
582
  isValid(raw, ctx, path) {
169
- if (this.values.includes(raw)) return true;
170
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected one of " + this.values.map((v) => JSON.stringify(v)).join(" | ") + ", got " + JSON.stringify(raw));
583
+ if (typeof raw === "string") return true;
584
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected string, got " + typeof raw);
585
+ return false;
586
+ }
587
+ };
588
+ var Num = class extends Base {
589
+ constructor(_default = 0) {
590
+ super();
591
+ this._default = _default;
592
+ }
593
+ sanitize(raw) {
594
+ return typeof raw === "number" && isFinite(raw) ? raw : this._default;
595
+ }
596
+ isValid(raw, ctx, path) {
597
+ if (typeof raw === "number" && isFinite(raw)) return true;
598
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected finite number, got " + JSON.stringify(raw));
599
+ return false;
600
+ }
601
+ };
602
+ var Bool = class extends Base {
603
+ constructor(_default = false) {
604
+ super();
605
+ this._default = _default;
606
+ }
607
+ sanitize(raw) {
608
+ return typeof raw === "boolean" ? raw : this._default;
609
+ }
610
+ isValid(raw, ctx, path) {
611
+ if (typeof raw === "boolean") return true;
612
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected boolean, got " + typeof raw);
613
+ return false;
614
+ }
615
+ };
616
+ var Literal = class extends Base {
617
+ constructor(value) {
618
+ super();
619
+ this.value = value;
620
+ this._default = value;
621
+ }
622
+ sanitize(raw) {
623
+ return raw === this.value ? this.value : this._default;
624
+ }
625
+ isValid(raw, ctx, path) {
626
+ if (raw === this.value) return true;
627
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected " + JSON.stringify(this.value) + ", got " + JSON.stringify(raw));
628
+ return false;
629
+ }
630
+ };
631
+ var Enum = class extends Base {
632
+ constructor(values, defaultVal) {
633
+ super();
634
+ this.values = values;
635
+ this._default = defaultVal != null ? defaultVal : values[0];
636
+ }
637
+ sanitize(raw) {
638
+ return this.values.includes(raw) ? raw : this._default;
639
+ }
640
+ isValid(raw, ctx, path) {
641
+ if (this.values.includes(raw)) return true;
642
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected one of " + this.values.map((v) => JSON.stringify(v)).join(" | ") + ", got " + JSON.stringify(raw));
171
643
  return false;
172
644
  }
173
645
  };
@@ -712,6 +1184,22 @@ var PixodeskAnimator = (() => {
712
1184
  styles: px.record(px.any()).optional(),
713
1185
  glyphs: px.record(PxGlyphFontSchema).optional()
714
1186
  }));
1187
+ var PX_SCROLL_PHASES = ["cover", "contain", "entry", "exit", "entry-crossing", "exit-crossing"];
1188
+ var PxScrollRangePointSchema = implementsInterface()(px.object({
1189
+ phase: px.enum(PX_SCROLL_PHASES).optional(),
1190
+ fraction: px.number().optional()
1191
+ }));
1192
+ var PxScrollRangeSchema = px.object({
1193
+ start: PxScrollRangePointSchema.optional(),
1194
+ end: PxScrollRangePointSchema.optional()
1195
+ });
1196
+ var PxScrollSchema = implementsInterface()(px.object({
1197
+ driver: px.enum(["custom", "native"]).optional(),
1198
+ kind: px.enum(["view", "scroll"]).optional(),
1199
+ axis: px.enum(["block", "inline", "x", "y"]).optional(),
1200
+ source: px.enum(["nearest", "root"]).optional(),
1201
+ range: PxScrollRangeSchema.optional()
1202
+ }));
715
1203
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
716
1204
  mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.waapi, PxAnimatorMode.frames]).optional(),
717
1205
  duration: px.number().optional(),
@@ -727,6 +1215,7 @@ var PixodeskAnimator = (() => {
727
1215
  definitions: PxDefsSchema.optional(),
728
1216
  animateById: px.record(PxElementAnimationSchema).optional(),
729
1217
  timelineSource: px.string().optional(),
1218
+ scroll: PxScrollSchema.optional(),
730
1219
  debugInstName: px.string().optional()
731
1220
  }));
732
1221
  var PxBindingSchema = implementsInterface()(px.object({
@@ -804,532 +1293,116 @@ var PixodeskAnimator = (() => {
804
1293
  // Contextual kind — the `type` convention, see `PxNodeBase.type`.
805
1294
  type: px.enum([PxCloneType.content]).optional(),
806
1295
  sourceId: px.string().optional(),
807
- retime: PxRetimeEffectSchema.optional()
808
- }));
809
- var PxGradientStopSchema = implementsInterface()(px.object({
810
- offset: px.number(),
811
- color: px.string()
812
- }));
813
- var PxAnimatableGradientStopsSchema = px.union([
814
- px.array(PxGradientStopSchema),
815
- px.object({ value: px.array(PxGradientStopSchema) }),
816
- PxPropertyAnimationSchema
817
- ]);
818
- var PxFillGradientEffectSchema = implementsInterface()(px.object({
819
- // Contextual kind — the `type` convention, see `PxNodeBase.type`.
820
- type: px.enum([PxGradientType.linear, PxGradientType.radial]),
821
- p1: PxAnimatableVec2Schema.optional(),
822
- p2: PxAnimatableVec2Schema.optional(),
823
- c: PxAnimatableVec2Schema.optional(),
824
- r: PxAnimatableNumberSchema.optional(),
825
- fp: PxAnimatableVec2Schema.optional(),
826
- stops: PxAnimatableGradientStopsSchema.optional(),
827
- gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
828
- spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
829
- gradientTransform: px.string().optional()
830
- }));
831
- var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
832
- var PxTextPathEffectSchema = implementsInterface()(px.object({
833
- path: px.string(),
834
- pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
835
- lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
836
- method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
837
- spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact]).optional(),
838
- startOffset: PxAnimatableNumberSchema.optional(),
839
- textLength: PxAnimatableNumberSchema.optional()
840
- }));
841
- var PxTextEffectSchema = implementsInterface()(px.object({
842
- useGlyphs: px.boolean().optional()
843
- }));
844
- var PxEffectsSchema = implementsInterface()(px.object({
845
- transformBy: PxTransformByEffectSchema.optional(),
846
- repeater: PxRepeaterEffectSchema.optional(),
847
- maskedBy: PxMaskedByEffectSchema.optional(),
848
- clipPath: PxClipPathEffectSchema.optional(),
849
- trimPath: PxTrimPathEffectSchema.optional(),
850
- clone: PxCloneEffectSchema.optional(),
851
- fillGradient: PxFillGradientEffectSchema.optional(),
852
- strokeGradient: PxStrokeGradientEffectSchema.optional(),
853
- textPath: PxTextPathEffectSchema.optional(),
854
- text: PxTextEffectSchema.optional()
855
- }));
856
- function validateNodeEffects(root, opts) {
857
- const warnings = [];
858
- const walk = (node, path) => {
859
- if (node && node.effects) {
860
- const ctx = { errors: [], warnings: [], strict: !!(opts == null ? void 0 : opts.strict) };
861
- const ok = PxEffectsSchema.isValid(node.effects, ctx, [path + ".effects"]);
862
- if (!ok) {
863
- for (const err of ctx.errors) warnings.push(err);
864
- }
865
- }
866
- if (node && Array.isArray(node.children)) {
867
- node.children.forEach((c, i) => walk(c, path + ".children[" + i + "]"));
868
- }
869
- };
870
- walk(root, "root");
871
- return warnings;
872
- }
873
- var PxNodeBase = px.openObject({
874
- // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for "what
875
- // kind of thing is this", discriminated by its CARRIER — here the node TAG
876
- // (`rect`, `text`), and inside a sub-object that object's kind (`clone.type`,
877
- // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so
878
- // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)
879
- // would add words that all mean "type" and still need the carrier to read.
880
- // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums
881
- // (issues V3), never of distinct key names.
882
- type: px.string(),
883
- id: px.string().optional(),
884
- meta: px.any().optional(),
885
- // Player-effects bucket emitted by the Editor's lightweight design format.
886
- // Consumed and removed by `applyPlayerEffects` before any other normalisation
887
- // (see `createAnimatorImpl`), so downstream code never sees it.
888
- effects: PxEffectsSchema.optional(),
889
- // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
890
- // string ref / array of refs / inline definition / mixed array; mirrors
891
- // `animator.animateById` map values and what `processNode` resolves at runtime.
892
- animate: PxElementAnimationSchema.optional(),
893
- style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
894
- }, PxAttrValueSchema);
895
- var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
896
- children: px.lazy(() => px.array(PxNodeSchema), []).optional()
897
- }), PxAttrValueSchema);
898
- var PxSvgNodeExtra = px.object({
899
- // `"100%"` and other SVG length strings are legal here — a number-only slot rejected
900
- // real documents (e.g. apple-store-look-14-main.json) at the root <svg>.
901
- width: px.union([px.number(), px.string()]).optional(),
902
- height: px.union([px.number(), px.string()]).optional(),
903
- viewBox: px.string().optional(),
904
- animator: PxAnimatorConfigSchema.optional()
905
- });
906
- var PxAnimatedSvgDocumentSchema = px.openObject(__spreadProps(__spreadValues(__spreadValues({}, PxNodeBase._shape), PxSvgNodeExtra._shape), {
907
- type: px.literal("svg"),
908
- // override string → literal to require 'svg'
909
- children: px.array(PxNodeSchema).optional()
910
- }), PxAttrValueSchema);
911
- var PxBezierPathSchema = implementsInterface()(px.object({
912
- v: px.array(px.array(px.number())),
913
- i: px.array(px.array(px.number())).optional(),
914
- o: px.array(px.array(px.number())).optional(),
915
- c: px.boolean().optional()
916
- }));
917
-
918
- // ../svg-animator-core/src/PxAnimatorUtil.ts
919
- function bezierToSvgPath(path, forceCurves = false) {
920
- var _a, _b, _c, _d;
921
- const v = path.v;
922
- const i = path.i;
923
- const o = path.o;
924
- const c = path.c;
925
- if (!v.length) return "";
926
- const d = [];
927
- const len = v.length;
928
- d.push("M" + v[0][0] + "," + v[0][1]);
929
- for (let idx = 1; idx < len; idx++) {
930
- const prevV = v[idx - 1];
931
- const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
932
- const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
933
- const currV = v[idx];
934
- const isLine = !forceCurves && (prevO[0] === prevV[0] && prevO[1] === prevV[1]) && (currI[0] === currV[0] && currI[1] === currV[1]);
935
- if (isLine) {
936
- d.push("L" + currV[0] + "," + currV[1]);
937
- } else {
938
- d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
939
- }
940
- }
941
- if (c && len > 0) {
942
- const lastV = v[len - 1];
943
- const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
944
- const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
945
- const firstV = v[0];
946
- const isLine = !forceCurves && (lastO[0] === lastV[0] && lastO[1] === lastV[1]) && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
947
- if (!isLine) {
948
- d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
949
- }
950
- d.push("z");
951
- }
952
- return d.join("");
953
- }
954
- function interpolateNum(a, b, t) {
955
- return a + (b - a) * t;
956
- }
957
- function interpolateVec(a, b, t) {
958
- const res = [];
959
- const count = Math.max(a.length, b.length);
960
- for (let i = 0; i < count; i++) {
961
- res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
962
- }
963
- return res;
964
- }
965
- function interpolateColor(a, b, t) {
966
- return [
967
- interpolateNum(a[0] || 0, b[0] || 0, t),
968
- interpolateNum(a[1] || 0, b[1] || 0, t),
969
- interpolateNum(a[2] || 0, b[2] || 0, t),
970
- interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
971
- ];
972
- }
973
- function interpolateBeziers(paths1, paths2, progress) {
974
- const count = Math.max(paths1.length, paths2.length);
975
- const res = [];
976
- for (let i = 0; i < count; i++) {
977
- res.push(interpolateBezier(paths1[i], paths2[i], progress));
978
- }
979
- return res;
980
- }
981
- function interpolateBezier(path1, path2, progress) {
982
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
983
- if (!path1 || !path2) return path1 || path2 || { v: [] };
984
- const t = Math.min(Math.max(progress, 0), 1);
985
- const len = Math.min(path1.v.length, path2.v.length);
986
- const v = [];
987
- const i = [];
988
- const o = [];
989
- for (let idx = 0; idx < len; idx++) {
990
- const v1 = path1.v[idx];
991
- const v2 = path2.v[idx];
992
- v.push(interpolateVec(v1, v2, t));
993
- const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
994
- const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
995
- i.push(interpolateVec(i1, i2, t));
996
- const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
997
- const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
998
- o.push(interpolateVec(o1, o2, t));
999
- }
1000
- return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
1001
- }
1002
- function remap(value, inMin, inMax, outMin, outMax) {
1003
- if (inMax === inMin) return outMin;
1004
- const t = (value - inMin) / (inMax - inMin);
1005
- return outMin + t * (outMax - outMin);
1006
- }
1007
- function solveCubicBezierX(p1x, p2x, x) {
1008
- if (x <= 0) return 0;
1009
- if (x >= 1) return 1;
1010
- const cx = 3 * p1x;
1011
- const bx = 3 * (p2x - p1x) - cx;
1012
- const ax = 1 - cx - bx;
1013
- function sampleX(t) {
1014
- return ((ax * t + bx) * t + cx) * t;
1015
- }
1016
- function sampleDX(t) {
1017
- return (3 * ax * t + 2 * bx) * t + cx;
1018
- }
1019
- let t2 = x;
1020
- let t0 = 0;
1021
- let t1 = 1;
1022
- for (let i = 0; i < 8; i++) {
1023
- const x2 = sampleX(t2) - x;
1024
- if (Math.abs(x2) < 1e-6) return t2;
1025
- const d2 = sampleDX(t2);
1026
- if (Math.abs(d2) < 1e-6) break;
1027
- t2 -= x2 / d2;
1028
- }
1029
- t2 = x;
1030
- while (t0 < t1) {
1031
- const x2 = sampleX(t2);
1032
- if (Math.abs(x2 - x) < 1e-6) return t2;
1033
- if (x > x2) t0 = t2;
1034
- else t1 = t2;
1035
- t2 = (t1 + t0) / 2;
1036
- }
1037
- return t2;
1038
- }
1039
- function cubicBezier(easing) {
1040
- const [p1x, p1y, p2x, p2y] = easing;
1041
- const cy = 3 * p1y;
1042
- const by = 3 * (p2y - p1y) - cy;
1043
- const ay = 1 - cy - by;
1044
- function sampleCurveY(t) {
1045
- return ((ay * t + by) * t + cy) * t;
1046
- }
1047
- return function(x) {
1048
- return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
1049
- };
1050
- }
1051
- function lerp2(a, b, t) {
1052
- return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
1053
- }
1054
- function subdivideCubicBezier(p0, p1, p2, p3, t) {
1055
- const q0 = lerp2(p0, p1, t);
1056
- const q1 = lerp2(p1, p2, t);
1057
- const q2 = lerp2(p2, p3, t);
1058
- const r0 = lerp2(q0, q1, t);
1059
- const r1 = lerp2(q1, q2, t);
1060
- const s = lerp2(r0, r1, t);
1061
- return {
1062
- left: [p0, q0, r0, s],
1063
- right: [s, r1, q2, p3]
1064
- };
1065
- }
1066
- function splitEasing(easing, xFraction) {
1067
- if (!easing) return { left: void 0, right: void 0 };
1068
- if (xFraction <= 0) return { left: void 0, right: easing };
1069
- if (xFraction >= 1) return { left: easing, right: void 0 };
1070
- const [x1, y1, x2, y2] = easing;
1071
- const t = solveCubicBezierX(x1, x2, xFraction);
1072
- const p0 = [0, 0];
1073
- const p1 = [x1, y1];
1074
- const p2 = [x2, y2];
1075
- const p3 = [1, 1];
1076
- const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
1077
- const sx = left[3][0];
1078
- const sy = left[3][1];
1079
- let leftEasing;
1080
- if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
1081
- leftEasing = [
1082
- left[1][0] / sx,
1083
- left[1][1] / sy,
1084
- left[2][0] / sx,
1085
- left[2][1] / sy
1086
- ];
1087
- }
1088
- let rightEasing;
1089
- const rx = 1 - sx;
1090
- const ry = 1 - sy;
1091
- if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
1092
- rightEasing = [
1093
- (right[1][0] - sx) / rx,
1094
- (right[1][1] - sy) / ry,
1095
- (right[2][0] - sx) / rx,
1096
- (right[2][1] - sy) / ry
1097
- ];
1098
- }
1099
- return { left: leftEasing, right: rightEasing };
1100
- }
1101
- function reverseEasing(easing) {
1102
- if (!easing) return void 0;
1103
- return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
1104
- }
1105
- function toRGBA(color) {
1106
- const r = Math.round(color[0] * 255);
1107
- const g = Math.round(color[1] * 255);
1108
- const b = Math.round(color[2] * 255);
1109
- return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
1110
- }
1111
- function parseRgba(s) {
1112
- var _a;
1113
- const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
1114
- if (!inner) throw new Error("Invalid rgb/rgba format");
1115
- const parts = inner.split(",").map((v) => +v.trim());
1116
- return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
1117
- }
1118
- function parseHex(s) {
1119
- const hex = s.slice(1);
1120
- const isShort = hex.length <= 4;
1121
- const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
1122
- const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
1123
- const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
1124
- const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
1125
- const result = [
1126
- parseInt(r, 16) / 255,
1127
- parseInt(g, 16) / 255,
1128
- parseInt(b, 16) / 255
1129
- ];
1130
- if (a !== null) {
1131
- result.push(parseInt(a, 16) / 255);
1132
- }
1133
- return result;
1134
- }
1135
- function parseColor(s) {
1136
- if (!s) return void 0;
1137
- if (Array.isArray(s)) return s;
1138
- if (typeof s !== "string") return void 0;
1139
- if (s.startsWith("#")) {
1140
- return parseHex(s);
1141
- } else if (s.startsWith("rgb")) {
1142
- return parseRgba(s);
1143
- } else {
1144
- console.warn("Unsupported color format: " + s);
1145
- }
1146
- return void 0;
1147
- }
1148
- var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
1149
- var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
1150
- var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1151
- function composeTransformParts(parts, opts) {
1152
- var _a;
1153
- if (!parts) return "";
1154
- const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
1155
- const segs = [];
1156
- const t = parts.translate;
1157
- const o = parts.origin;
1158
- const r = parts.rotate;
1159
- const k = parts.skew;
1160
- const s = parts.scale;
1161
- const tu = withUnits ? "px" : "";
1162
- const ru = withUnits ? "deg" : "";
1163
- if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
1164
- if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
1165
- if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
1166
- if (k !== void 0 && k !== null) segs.push("skewX(" + k + ru + ")");
1167
- if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
1168
- if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
1169
- return segs.join("");
1170
- }
1171
- var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1172
- var DEFAULT_DURATION_MS = 1e3;
1173
- function kebabToCamelCaseWord(kebab) {
1174
- return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
1175
- }
1176
- function isCamelCaseWord(word) {
1177
- return !word.includes("-") && /[a-z][A-Z]/.test(word);
1178
- }
1179
- var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
1180
- // Transform/positioning
1181
- "viewBox",
1182
- "preserveAspectRatio",
1183
- // Gradient
1184
- "gradientUnits",
1185
- "gradientTransform",
1186
- "spreadMethod",
1187
- // Pattern
1188
- "patternUnits",
1189
- "patternContentUnits",
1190
- "patternTransform",
1191
- // Clipping/masking
1192
- "clipPathUnits",
1193
- "maskUnits",
1194
- "maskContentUnits",
1195
- // Marker (SVG spec keeps these camelCase, like viewBox)
1196
- "markerUnits",
1197
- "markerWidth",
1198
- "markerHeight",
1199
- "refX",
1200
- "refY",
1201
- // Text
1202
- "textLength",
1203
- "lengthAdjust",
1204
- "startOffset",
1205
- // Filter
1206
- "filterUnits",
1207
- "primitiveUnits",
1208
- "tableValues",
1209
- // feFuncR/G/B/A transfer table (type="table")
1210
- "stdDeviation",
1211
- "baseFrequency",
1212
- "numOctaves",
1213
- "surfaceScale",
1214
- "diffuseConstant",
1215
- "specularConstant",
1216
- "specularExponent",
1217
- "kernelMatrix",
1218
- "kernelUnitLength",
1219
- "edgeMode",
1220
- "preserveAlpha",
1221
- "targetX",
1222
- "targetY"
1223
- // // Animation
1224
- // 'attributeName',
1225
- // 'attributeType',
1226
- // 'calcMode',
1227
- // 'keyTimes',
1228
- // 'keySplines',
1229
- // 'repeatCount',
1230
- // 'repeatDur'
1296
+ retime: PxRetimeEffectSchema.optional()
1297
+ }));
1298
+ var PxGradientStopSchema = implementsInterface()(px.object({
1299
+ offset: px.number(),
1300
+ color: px.string()
1301
+ }));
1302
+ var PxAnimatableGradientStopsSchema = px.union([
1303
+ px.array(PxGradientStopSchema),
1304
+ px.object({ value: px.array(PxGradientStopSchema) }),
1305
+ PxPropertyAnimationSchema
1231
1306
  ]);
1232
- function camelCaseToKebabWordIfNeeded(camel) {
1233
- return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1234
- }
1235
- function clamp(value, min, max) {
1236
- return Math.max(min, Math.min(value, max));
1237
- }
1238
- function bezier2D_pointAt(P0, P1, P2, P3, t) {
1239
- if (t <= 0) return [P0[0], P0[1]];
1240
- if (t >= 1) return [P3[0], P3[1]];
1241
- const u = 1 - t;
1242
- const u2 = u * u;
1243
- const u3 = u2 * u;
1244
- const t2 = t * t;
1245
- const t3 = t2 * t;
1246
- const w0 = u3;
1247
- const w1 = 3 * t * u2;
1248
- const w2 = 3 * t2 * u;
1249
- const w3 = t3;
1250
- return [
1251
- w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
1252
- w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
1253
- ];
1254
- }
1255
- var BEZIER_T_NUDGE = 1e-4;
1256
- function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
1257
- const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
1258
- if (result[0] === 0 && result[1] === 0) {
1259
- const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
1260
- return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
1261
- }
1262
- return result;
1263
- }
1264
- function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
1265
- const u = 1 - t;
1266
- const a = 3 * u * u;
1267
- const b = 6 * t * u;
1268
- const c = 3 * t * t;
1269
- return [
1270
- a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
1271
- a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
1272
- ];
1273
- }
1274
- function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
1275
- const n = steps + 1;
1276
- const ts = new Float64Array(n);
1277
- const ds = new Float64Array(n);
1278
- let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
1279
- ts[0] = 0;
1280
- ds[0] = 0;
1281
- let cum = 0;
1282
- for (let i = 1; i < n; i++) {
1283
- const t = i / steps;
1284
- const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
1285
- const dx = cur[0] - prev[0];
1286
- const dy = cur[1] - prev[1];
1287
- cum += Math.sqrt(dx * dx + dy * dy);
1288
- ts[i] = t;
1289
- ds[i] = cum;
1290
- prev = cur;
1291
- }
1292
- return { ts, ds };
1293
- }
1294
- function bezier2D_tForDistance(lut, distance) {
1295
- const { ts, ds } = lut;
1296
- const last = ds.length - 1;
1297
- if (distance <= 0) return ts[0];
1298
- if (distance >= ds[last]) return ts[last];
1299
- let lo = 1;
1300
- let hi = last;
1301
- while (lo < hi) {
1302
- const mid = lo + hi >>> 1;
1303
- if (ds[mid] < distance) lo = mid + 1;
1304
- else hi = mid;
1305
- }
1306
- const dPrev = ds[hi - 1];
1307
- const dCur = ds[hi];
1308
- const span = dCur - dPrev;
1309
- const frac = span > 0 ? (distance - dPrev) / span : 0;
1310
- return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
1311
- }
1312
- function bezier2D_arcAtT(lut, t) {
1313
- const { ts, ds } = lut;
1314
- const last = ts.length - 1;
1315
- if (t <= ts[0]) return ds[0];
1316
- if (t >= ts[last]) return ds[last];
1317
- let lo = 1, hi = last;
1318
- while (lo < hi) {
1319
- const mid = lo + hi >>> 1;
1320
- if (ts[mid] < t) lo = mid + 1;
1321
- else hi = mid;
1322
- }
1323
- const tPrev = ts[hi - 1];
1324
- const span = ts[hi] - tPrev;
1325
- const frac = span > 0 ? (t - tPrev) / span : 0;
1326
- return ds[hi - 1] + frac * (ds[hi] - ds[hi - 1]);
1327
- }
1328
- function invertEasing(easing) {
1329
- if (!easing) return (y) => y;
1330
- const flipped = [easing[1], easing[0], easing[3], easing[2]];
1331
- return cubicBezier(flipped);
1307
+ var PxFillGradientEffectSchema = implementsInterface()(px.object({
1308
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
1309
+ type: px.enum([PxGradientType.linear, PxGradientType.radial]),
1310
+ p1: PxAnimatableVec2Schema.optional(),
1311
+ p2: PxAnimatableVec2Schema.optional(),
1312
+ c: PxAnimatableVec2Schema.optional(),
1313
+ r: PxAnimatableNumberSchema.optional(),
1314
+ fp: PxAnimatableVec2Schema.optional(),
1315
+ stops: PxAnimatableGradientStopsSchema.optional(),
1316
+ gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
1317
+ spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
1318
+ gradientTransform: px.string().optional()
1319
+ }));
1320
+ var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
1321
+ var PxTextPathEffectSchema = implementsInterface()(px.object({
1322
+ path: px.string(),
1323
+ pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
1324
+ lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
1325
+ method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
1326
+ spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact]).optional(),
1327
+ startOffset: PxAnimatableNumberSchema.optional(),
1328
+ textLength: PxAnimatableNumberSchema.optional()
1329
+ }));
1330
+ var PxTextEffectSchema = implementsInterface()(px.object({
1331
+ useGlyphs: px.boolean().optional()
1332
+ }));
1333
+ var PxEffectsSchema = implementsInterface()(px.object({
1334
+ transformBy: PxTransformByEffectSchema.optional(),
1335
+ repeater: PxRepeaterEffectSchema.optional(),
1336
+ maskedBy: PxMaskedByEffectSchema.optional(),
1337
+ clipPath: PxClipPathEffectSchema.optional(),
1338
+ trimPath: PxTrimPathEffectSchema.optional(),
1339
+ clone: PxCloneEffectSchema.optional(),
1340
+ fillGradient: PxFillGradientEffectSchema.optional(),
1341
+ strokeGradient: PxStrokeGradientEffectSchema.optional(),
1342
+ textPath: PxTextPathEffectSchema.optional(),
1343
+ text: PxTextEffectSchema.optional()
1344
+ }));
1345
+ function validateNodeEffects(root, opts) {
1346
+ const warnings = [];
1347
+ const walk = (node, path) => {
1348
+ if (node && node.effects) {
1349
+ const ctx = { errors: [], warnings: [], strict: !!(opts == null ? void 0 : opts.strict) };
1350
+ const ok = PxEffectsSchema.isValid(node.effects, ctx, [path + ".effects"]);
1351
+ if (!ok) {
1352
+ for (const err of ctx.errors) warnings.push(err);
1353
+ }
1354
+ }
1355
+ if (node && Array.isArray(node.children)) {
1356
+ node.children.forEach((c, i) => walk(c, path + ".children[" + i + "]"));
1357
+ }
1358
+ };
1359
+ walk(root, "root");
1360
+ return warnings;
1332
1361
  }
1362
+ var PxNodeBase = px.openObject({
1363
+ // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for "what
1364
+ // kind of thing is this", discriminated by its CARRIER — here the node TAG
1365
+ // (`rect`, `text`), and inside a sub-object that object's kind (`clone.type`,
1366
+ // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so
1367
+ // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)
1368
+ // would add words that all mean "type" and still need the carrier to read.
1369
+ // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums
1370
+ // (issues V3), never of distinct key names.
1371
+ type: px.string(),
1372
+ id: px.string().optional(),
1373
+ meta: px.any().optional(),
1374
+ // Player-effects bucket emitted by the Editor's lightweight design format.
1375
+ // Consumed and removed by `applyPlayerEffects` before any other normalisation
1376
+ // (see `createAnimatorImpl`), so downstream code never sees it.
1377
+ effects: PxEffectsSchema.optional(),
1378
+ // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
1379
+ // string ref / array of refs / inline definition / mixed array; mirrors
1380
+ // `animator.animateById` map values and what `processNode` resolves at runtime.
1381
+ animate: PxElementAnimationSchema.optional(),
1382
+ style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
1383
+ }, PxAttrValueSchema);
1384
+ var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
1385
+ children: px.lazy(() => px.array(PxNodeSchema), []).optional()
1386
+ }), PxAttrValueSchema);
1387
+ var PxSvgNodeExtra = px.object({
1388
+ // `"100%"` and other SVG length strings are legal here — a number-only slot rejected
1389
+ // real documents (e.g. apple-store-look-14-main.json) at the root <svg>.
1390
+ width: px.union([px.number(), px.string()]).optional(),
1391
+ height: px.union([px.number(), px.string()]).optional(),
1392
+ viewBox: px.string().optional(),
1393
+ animator: PxAnimatorConfigSchema.optional()
1394
+ });
1395
+ var PxAnimatedSvgDocumentSchema = px.openObject(__spreadProps(__spreadValues(__spreadValues({}, PxNodeBase._shape), PxSvgNodeExtra._shape), {
1396
+ type: px.literal("svg"),
1397
+ // override string → literal to require 'svg'
1398
+ children: px.array(PxNodeSchema).optional()
1399
+ }), PxAttrValueSchema);
1400
+ var PxBezierPathSchema = implementsInterface()(px.object({
1401
+ v: px.array(px.array(px.number())),
1402
+ i: px.array(px.array(px.number())).optional(),
1403
+ o: px.array(px.array(px.number())).optional(),
1404
+ c: px.boolean().optional()
1405
+ }));
1333
1406
 
1334
1407
  // ../svg-animator-core/src/PxIdUtil.ts
1335
1408
  var _idCounter = 0;
@@ -5732,7 +5805,13 @@ var PixodeskAnimator = (() => {
5732
5805
  const api = __spreadProps(__spreadValues({}, basicApi), {
5733
5806
  "getRootElement": () => rootElement || null
5734
5807
  });
5735
- if (config.trigger) setupAnimationTriggers(api, config.trigger);
5808
+ if (config.trigger) {
5809
+ if (isScrollTimeline(config)) {
5810
+ console.warn("scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
5811
+ } else {
5812
+ setupAnimationTriggers(api, config.trigger);
5813
+ }
5814
+ }
5736
5815
  return api;
5737
5816
  }
5738
5817
  function createDomAdapter(rootElement) {
@@ -5860,7 +5939,7 @@ var PixodeskAnimator = (() => {
5860
5939
  }
5861
5940
  return result;
5862
5941
  }
5863
- function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs) {
5942
+ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs, scrollTimeline) {
5864
5943
  var _a;
5865
5944
  const config = getAnimatorConfig(doc) || {};
5866
5945
  if (!rootElement) {
@@ -5919,7 +5998,12 @@ var PixodeskAnimator = (() => {
5919
5998
  if (keyframes.length > 0) {
5920
5999
  try {
5921
6000
  const effect = new KeyframeEffect(element, keyframes, effectOptions);
5922
- const anim = new Animation(effect, document.timeline);
6001
+ const anim = new Animation(effect, scrollTimeline ? scrollTimeline.timeline : document.timeline);
6002
+ if (scrollTimeline) {
6003
+ const a = anim;
6004
+ if (scrollTimeline.rangeStart) a.rangeStart = scrollTimeline.rangeStart;
6005
+ if (scrollTimeline.rangeEnd) a.rangeEnd = scrollTimeline.rangeEnd;
6006
+ }
5923
6007
  if (callbacks == null ? void 0 : callbacks.onFinish) anim.onfinish = () => {
5924
6008
  var _a2;
5925
6009
  if (finishNotified) return;
@@ -6013,11 +6097,136 @@ var PixodeskAnimator = (() => {
6013
6097
  }
6014
6098
  };
6015
6099
  if (config.trigger) {
6016
- setupAnimationTriggers(api, config.trigger);
6100
+ if (config.timelineSource === "scroll") {
6101
+ console.warn("scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
6102
+ } else {
6103
+ setupAnimationTriggers(api, config.trigger);
6104
+ }
6105
+ }
6106
+ if (scrollTimeline) {
6107
+ animations.forEach((a) => a.play());
6017
6108
  }
6018
6109
  return api;
6019
6110
  }
6020
6111
 
6112
+ // src/PxScrollDriver.ts
6113
+ function nativeRangeOffset(point, defaultFraction, view) {
6114
+ var _a, _b, _c;
6115
+ const fraction = typeof (point == null ? void 0 : point.fraction) === "number" ? point.fraction : defaultFraction;
6116
+ const pct = (_b = (_a = globalThis.CSS) == null ? void 0 : _a.percent) == null ? void 0 : _b.call(_a, fraction * 100);
6117
+ if (pct === void 0) return void 0;
6118
+ return view ? { rangeName: (_c = point == null ? void 0 : point.phase) != null ? _c : "cover", offset: pct } : { offset: pct };
6119
+ }
6120
+ function createNativeScrollTimeline(subject, config) {
6121
+ var _a, _b, _c, _d;
6122
+ if (!config || !isScrollTimeline(config)) return null;
6123
+ const scroll = config.scroll || {};
6124
+ const kind = (_a = scroll.kind) != null ? _a : "view";
6125
+ const g = globalThis;
6126
+ const view = kind === "view";
6127
+ const Ctor = view ? g.ViewTimeline : g.ScrollTimeline;
6128
+ if (typeof Ctor !== "function") return null;
6129
+ const axis = (_b = scroll.axis) != null ? _b : "block";
6130
+ let timeline;
6131
+ try {
6132
+ if (view) {
6133
+ timeline = new Ctor({ subject, axis });
6134
+ } else {
6135
+ const source = scroll.source === "root" ? documentScroller() : findNearestScroller(subject, "y") || findNearestScroller(subject, "x") || documentScroller();
6136
+ timeline = new Ctor({ source, axis });
6137
+ }
6138
+ } catch (e) {
6139
+ console.warn("scroll timeline: native timeline construction failed \u2014 falling back to the custom driver", e);
6140
+ return null;
6141
+ }
6142
+ return {
6143
+ timeline,
6144
+ rangeStart: nativeRangeOffset((_c = scroll.range) == null ? void 0 : _c.start, 0, view),
6145
+ rangeEnd: nativeRangeOffset((_d = scroll.range) == null ? void 0 : _d.end, 1, view)
6146
+ };
6147
+ }
6148
+ function findNearestScroller(el, axis) {
6149
+ for (let p = el.parentElement; p; p = p.parentElement) {
6150
+ const style = getComputedStyle(p);
6151
+ const overflow = axis === "y" ? style.overflowY : style.overflowX;
6152
+ if (overflow === "auto" || overflow === "scroll" || overflow === "hidden" || overflow === "overlay") {
6153
+ return p;
6154
+ }
6155
+ }
6156
+ return null;
6157
+ }
6158
+ function documentScroller() {
6159
+ return document.scrollingElement || document.documentElement;
6160
+ }
6161
+ function createScrollDriver(subject, config, onProgress) {
6162
+ var _a;
6163
+ if (!config || !isScrollTimeline(config)) return null;
6164
+ const scroll = config.scroll || {};
6165
+ const kind = (_a = scroll.kind) != null ? _a : "view";
6166
+ const nearest = findNearestScroller(subject, "y") || findNearestScroller(subject, "x");
6167
+ const scroller = kind === "scroll" && scroll.source === "root" ? documentScroller() : nearest || documentScroller();
6168
+ const isRootScroller = scroller === documentScroller();
6169
+ const axis = scrollResolveAxis(scroll.axis, getComputedStyle(scroller).writingMode);
6170
+ const compute = () => {
6171
+ if (kind === "scroll") {
6172
+ const offset = axis === "y" ? scroller.scrollTop : scroller.scrollLeft;
6173
+ const maxOffset = axis === "y" ? scroller.scrollHeight - scroller.clientHeight : scroller.scrollWidth - scroller.clientWidth;
6174
+ return scrollOffsetProgress(offset, maxOffset, scroll.range);
6175
+ }
6176
+ const subjectRect = subject.getBoundingClientRect();
6177
+ let portStart, portSize;
6178
+ if (isRootScroller) {
6179
+ portStart = 0;
6180
+ portSize = axis === "y" ? document.documentElement.clientHeight : document.documentElement.clientWidth;
6181
+ } else {
6182
+ const portRect = scroller.getBoundingClientRect();
6183
+ portStart = axis === "y" ? portRect.top : portRect.left;
6184
+ portSize = axis === "y" ? scroller.clientHeight : scroller.clientWidth;
6185
+ }
6186
+ const subjectStart = (axis === "y" ? subjectRect.top : subjectRect.left) - portStart;
6187
+ const subjectSize = axis === "y" ? subjectRect.height : subjectRect.width;
6188
+ return scrollViewProgress(subjectStart, subjectSize, portSize, scroll.range);
6189
+ };
6190
+ let rafId = null;
6191
+ let destroyed = false;
6192
+ const tick = () => {
6193
+ rafId = null;
6194
+ if (destroyed) return;
6195
+ onProgress(compute());
6196
+ };
6197
+ const schedule = () => {
6198
+ if (destroyed || rafId !== null) return;
6199
+ rafId = requestAnimationFrame(tick);
6200
+ };
6201
+ const scrollTarget = isRootScroller ? window : scroller;
6202
+ scrollTarget.addEventListener("scroll", schedule, { passive: true });
6203
+ window.addEventListener("resize", schedule, { passive: true });
6204
+ let resizeObserver;
6205
+ if (typeof ResizeObserver !== "undefined") {
6206
+ resizeObserver = new ResizeObserver(schedule);
6207
+ resizeObserver.observe(subject);
6208
+ if (!isRootScroller) resizeObserver.observe(scroller);
6209
+ }
6210
+ const driver = {
6211
+ destroy: () => {
6212
+ if (destroyed) return;
6213
+ destroyed = true;
6214
+ scrollTarget.removeEventListener("scroll", schedule);
6215
+ window.removeEventListener("resize", schedule);
6216
+ resizeObserver == null ? void 0 : resizeObserver.disconnect();
6217
+ if (rafId !== null) {
6218
+ cancelAnimationFrame(rafId);
6219
+ rafId = null;
6220
+ }
6221
+ },
6222
+ refresh: () => {
6223
+ if (!destroyed) onProgress(compute());
6224
+ }
6225
+ };
6226
+ driver.refresh();
6227
+ return driver;
6228
+ }
6229
+
6021
6230
  // src/PxAnimatorBind.ts
6022
6231
  function finaliseAnimator(animatorConfig, callbacks, make) {
6023
6232
  let apiRef;
@@ -6040,6 +6249,44 @@ var PixodeskAnimator = (() => {
6040
6249
  }
6041
6250
  function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
6042
6251
  const animatorConfig = getAnimatorConfig(doc) || {};
6252
+ if (isScrollTimeline(animatorConfig)) {
6253
+ return finaliseAnimator(animatorConfig, callbacks, (cb) => {
6254
+ var _a, _b;
6255
+ if (animatorConfig.mode !== PxAnimatorMode.frames && ((_a = animatorConfig.scroll) == null ? void 0 : _a.driver) === "native" && rootElement) {
6256
+ const native = createNativeScrollTimeline(rootElement, animatorConfig);
6257
+ if (native) {
6258
+ const api2 = createWebApiAnimator(
6259
+ doc,
6260
+ cb,
6261
+ rootElement,
6262
+ animatorConfig.mode === PxAnimatorMode.waapi,
6263
+ native
6264
+ );
6265
+ if (api2) return api2;
6266
+ }
6267
+ }
6268
+ const api = (animatorConfig.mode !== PxAnimatorMode.frames ? createWebApiAnimator(doc, cb, rootElement, animatorConfig.mode === PxAnimatorMode.waapi) : null) || createFrameLoopAnimator(doc, adapter, cb, rootElement);
6269
+ const subject = ((_b = api.getRootElement) == null ? void 0 : _b.call(api)) || rootElement;
6270
+ if (subject) {
6271
+ const totalMs = scrollTotalDurationMs(animatorConfig);
6272
+ const driver = createScrollDriver(
6273
+ subject,
6274
+ animatorConfig,
6275
+ (progress) => api.setCurrentTime(progress * totalMs)
6276
+ );
6277
+ if (driver) {
6278
+ const destroy = api.destroy.bind(api);
6279
+ api.destroy = () => {
6280
+ driver.destroy();
6281
+ destroy();
6282
+ };
6283
+ }
6284
+ } else {
6285
+ console.warn("scroll timeline: no root element to observe \u2014 animation will stay at frame 0");
6286
+ }
6287
+ return api;
6288
+ });
6289
+ }
6043
6290
  return finaliseAnimator(animatorConfig, callbacks, (cb) => {
6044
6291
  if (animatorConfig.mode === PxAnimatorMode.frames) {
6045
6292
  return createFrameLoopAnimator(doc, adapter, cb, rootElement);