@pixodesk/svg-animator-web 1.0.27 → 1.0.29

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.
@@ -2258,6 +2258,10 @@ var PixodeskAnimator = (() => {
2258
2258
  if (!config || !isScrollTimeline(config)) return null;
2259
2259
  const scroll = config.scroll || {};
2260
2260
  const kind = (_a = scroll.kind) != null ? _a : "view";
2261
+ if (scroll.smoothing) {
2262
+ console.warn('scroll timeline: `smoothing` needs the built-in driver \u2014 ignoring `driver: "native"`');
2263
+ return null;
2264
+ }
2261
2265
  const g = globalThis;
2262
2266
  const view = kind === "view";
2263
2267
  const Ctor = view ? g.ViewTimeline : g.ScrollTimeline;
@@ -2266,7 +2270,7 @@ var PixodeskAnimator = (() => {
2266
2270
  let timeline;
2267
2271
  try {
2268
2272
  if (view) {
2269
- timeline = new Ctor({ subject, axis });
2273
+ timeline = new Ctor({ subject: resolveScrollSubject(subject, scroll.subject), axis });
2270
2274
  } else {
2271
2275
  const source = scroll.source === "root" ? documentScroller() : findNearestScroller(subject, "y") || findNearestScroller(subject, "x") || documentScroller();
2272
2276
  timeline = new Ctor({ source, axis });
@@ -2282,7 +2286,10 @@ var PixodeskAnimator = (() => {
2282
2286
  };
2283
2287
  }
2284
2288
  function findNearestScroller(el, axis) {
2289
+ const body = document.body;
2290
+ const root = document.documentElement;
2285
2291
  for (let p = el.parentElement; p; p = p.parentElement) {
2292
+ if (p === body || p === root) return null;
2286
2293
  const style = getComputedStyle(p);
2287
2294
  const overflow = axis === "y" ? style.overflowY : style.overflowX;
2288
2295
  if (overflow === "auto" || overflow === "scroll" || overflow === "hidden" || overflow === "overlay") {
@@ -2294,11 +2301,42 @@ var PixodeskAnimator = (() => {
2294
2301
  function documentScroller() {
2295
2302
  return document.scrollingElement || document.documentElement;
2296
2303
  }
2304
+ var SUBJECT_PARENT = "parent";
2305
+ var SUBJECT_SCROLLER = "scroller";
2306
+ function resolveScrollSubject(svgRoot, subject) {
2307
+ var _a, _b;
2308
+ const spec = subject == null ? void 0 : subject.trim();
2309
+ if (!spec) return svgRoot;
2310
+ if (spec === SUBJECT_PARENT) {
2311
+ let outermostPinned = null;
2312
+ for (let p = svgRoot.parentElement; p && p !== document.body; p = p.parentElement) {
2313
+ const position = getComputedStyle(p).position;
2314
+ if (position === "sticky" || position === "fixed") outermostPinned = p;
2315
+ }
2316
+ return (_b = (_a = outermostPinned == null ? void 0 : outermostPinned.parentElement) != null ? _a : svgRoot.parentElement) != null ? _b : svgRoot;
2317
+ }
2318
+ if (spec === SUBJECT_SCROLLER) {
2319
+ return findNearestScroller(svgRoot, "y") || findNearestScroller(svgRoot, "x") || documentScroller();
2320
+ }
2321
+ let found = null;
2322
+ try {
2323
+ found = document.querySelector(spec);
2324
+ } catch (e) {
2325
+ console.warn('scroll timeline: subject "' + spec + '" is not a valid selector \u2014 measuring the SVG itself');
2326
+ return svgRoot;
2327
+ }
2328
+ if (!found) {
2329
+ console.warn('scroll timeline: subject "' + spec + '" matched no element \u2014 measuring the SVG itself');
2330
+ return svgRoot;
2331
+ }
2332
+ return found;
2333
+ }
2297
2334
  function createScrollDriver(subject, config, onProgress) {
2298
- var _a;
2335
+ var _a, _b;
2299
2336
  if (!config || !isScrollTimeline(config)) return null;
2300
2337
  const scroll = config.scroll || {};
2301
2338
  const kind = (_a = scroll.kind) != null ? _a : "view";
2339
+ const measured = resolveScrollSubject(subject, scroll.subject);
2302
2340
  const nearest = findNearestScroller(subject, "y") || findNearestScroller(subject, "x");
2303
2341
  const scroller = kind === "scroll" && scroll.source === "root" ? documentScroller() : nearest || documentScroller();
2304
2342
  const isRootScroller = scroller === documentScroller();
@@ -2309,7 +2347,7 @@ var PixodeskAnimator = (() => {
2309
2347
  const maxOffset = axis === "y" ? scroller.scrollHeight - scroller.clientHeight : scroller.scrollWidth - scroller.clientWidth;
2310
2348
  return scrollOffsetProgress(offset, maxOffset, scroll.range);
2311
2349
  }
2312
- const subjectRect = subject.getBoundingClientRect();
2350
+ const subjectRect = measured.getBoundingClientRect();
2313
2351
  let portStart, portSize;
2314
2352
  if (isRootScroller) {
2315
2353
  portStart = 0;
@@ -2323,12 +2361,43 @@ var PixodeskAnimator = (() => {
2323
2361
  const subjectSize = axis === "y" ? subjectRect.height : subjectRect.width;
2324
2362
  return scrollViewProgress(subjectStart, subjectSize, portSize, scroll.range);
2325
2363
  };
2326
- let rafId = null;
2364
+ const smoothingSec = Math.max(0, (_b = scroll.smoothing) != null ? _b : 0) / 1e3;
2365
+ const SETTLE_EPSILON = 1e-3;
2327
2366
  let destroyed = false;
2367
+ let smoothed = null;
2368
+ let smoothRaf = null;
2369
+ let lastFrameMs = 0;
2370
+ const emit = (target) => {
2371
+ if (!smoothingSec) {
2372
+ onProgress(target);
2373
+ return;
2374
+ }
2375
+ if (smoothed === null) {
2376
+ smoothed = target;
2377
+ onProgress(target);
2378
+ return;
2379
+ }
2380
+ if (smoothRaf !== null) return;
2381
+ lastFrameMs = 0;
2382
+ const step = (nowMs) => {
2383
+ smoothRaf = null;
2384
+ if (destroyed) return;
2385
+ const dtSec = lastFrameMs ? Math.min(0.1, (nowMs - lastFrameMs) / 1e3) : 1 / 60;
2386
+ lastFrameMs = nowMs;
2387
+ const goal = compute();
2388
+ const k = 1 - Math.exp(-dtSec / smoothingSec);
2389
+ smoothed = smoothed + (goal - smoothed) * k;
2390
+ if (Math.abs(goal - smoothed) < SETTLE_EPSILON) smoothed = goal;
2391
+ onProgress(smoothed);
2392
+ if (smoothed !== goal) smoothRaf = requestAnimationFrame(step);
2393
+ };
2394
+ smoothRaf = requestAnimationFrame(step);
2395
+ };
2396
+ let rafId = null;
2328
2397
  const tick = () => {
2329
2398
  rafId = null;
2330
2399
  if (destroyed) return;
2331
- onProgress(compute());
2400
+ emit(compute());
2332
2401
  };
2333
2402
  const schedule = () => {
2334
2403
  if (destroyed || rafId !== null) return;
@@ -2340,7 +2409,8 @@ var PixodeskAnimator = (() => {
2340
2409
  let resizeObserver;
2341
2410
  if (typeof ResizeObserver !== "undefined") {
2342
2411
  resizeObserver = new ResizeObserver(schedule);
2343
- resizeObserver.observe(subject);
2412
+ resizeObserver.observe(measured);
2413
+ if (measured !== subject) resizeObserver.observe(subject);
2344
2414
  if (!isRootScroller) resizeObserver.observe(scroller);
2345
2415
  }
2346
2416
  const driver = {
@@ -2354,14 +2424,76 @@ var PixodeskAnimator = (() => {
2354
2424
  cancelAnimationFrame(rafId);
2355
2425
  rafId = null;
2356
2426
  }
2427
+ if (smoothRaf !== null) {
2428
+ cancelAnimationFrame(smoothRaf);
2429
+ smoothRaf = null;
2430
+ }
2357
2431
  },
2432
+ // `refresh` is a deliberate JUMP (attach, host relayout) — never eased.
2358
2433
  refresh: () => {
2359
- if (!destroyed) onProgress(compute());
2434
+ if (!destroyed) {
2435
+ smoothed = compute();
2436
+ onProgress(smoothed);
2437
+ }
2360
2438
  }
2361
2439
  };
2362
2440
  driver.refresh();
2363
2441
  return driver;
2364
2442
  }
2443
+ function pinScrollportHeight(svgRoot) {
2444
+ const scroller = findNearestScroller(svgRoot, "y");
2445
+ return scroller ? scroller.clientHeight : document.documentElement.clientHeight;
2446
+ }
2447
+ function applyScrollPin(svgRoot, scroll) {
2448
+ const styled = svgRoot;
2449
+ if (!(scroll == null ? void 0 : scroll.pin) || !styled.style) return () => {
2450
+ };
2451
+ const style = styled.style;
2452
+ const prevPosition = style.position;
2453
+ const prevTop = style.top;
2454
+ style.position = "sticky";
2455
+ const alignFactor = scroll.pinAlign === "center" ? 0.5 : scroll.pinAlign === "bottom" ? 1 : 0;
2456
+ const applyTop = () => {
2457
+ var _a;
2458
+ const extra = (_a = scroll.pinTop) != null ? _a : 0;
2459
+ if (!alignFactor) {
2460
+ style.top = extra + "px";
2461
+ return;
2462
+ }
2463
+ const portSize = pinScrollportHeight(svgRoot);
2464
+ const ownSize = svgRoot.getBoundingClientRect().height;
2465
+ style.top = Math.round((portSize - ownSize) * alignFactor + extra) + "px";
2466
+ };
2467
+ applyTop();
2468
+ let resizeObserver = null;
2469
+ const onWindowResize = alignFactor ? applyTop : null;
2470
+ if (alignFactor) {
2471
+ if (onWindowResize) window.addEventListener("resize", onWindowResize);
2472
+ if (typeof ResizeObserver !== "undefined") {
2473
+ resizeObserver = new ResizeObserver(applyTop);
2474
+ resizeObserver.observe(svgRoot);
2475
+ }
2476
+ }
2477
+ let wrapper = null;
2478
+ const parent = svgRoot.parentElement;
2479
+ if (scroll.pinDistance && scroll.pinDistance > 0 && parent) {
2480
+ wrapper = document.createElement("div");
2481
+ wrapper.setAttribute("data-px-pin", "");
2482
+ wrapper.style.height = scroll.pinDistance * 100 + "vh";
2483
+ parent.insertBefore(wrapper, svgRoot);
2484
+ wrapper.appendChild(svgRoot);
2485
+ }
2486
+ return () => {
2487
+ if (onWindowResize) window.removeEventListener("resize", onWindowResize);
2488
+ resizeObserver == null ? void 0 : resizeObserver.disconnect();
2489
+ style.position = prevPosition;
2490
+ style.top = prevTop;
2491
+ if (wrapper == null ? void 0 : wrapper.parentElement) {
2492
+ wrapper.parentElement.insertBefore(svgRoot, wrapper);
2493
+ wrapper.remove();
2494
+ }
2495
+ };
2496
+ }
2365
2497
 
2366
2498
  // src/PxAnimatorBind.ts
2367
2499
  function finaliseAnimator(animatorConfig, callbacks, make) {
@@ -2388,7 +2520,10 @@ var PixodeskAnimator = (() => {
2388
2520
  if (isScrollTimeline(animatorConfig)) {
2389
2521
  return finaliseAnimator(animatorConfig, callbacks, (cb) => {
2390
2522
  var _a, _b;
2523
+ let unpin = () => {
2524
+ };
2391
2525
  if (animatorConfig.mode !== PxAnimatorMode.frames && ((_a = animatorConfig.scroll) == null ? void 0 : _a.driver) === "native" && rootElement) {
2526
+ unpin = applyScrollPin(rootElement, animatorConfig.scroll);
2392
2527
  const native = createNativeScrollTimeline(rootElement, animatorConfig);
2393
2528
  if (native) {
2394
2529
  const api2 = createWebApiAnimator(
@@ -2398,12 +2533,23 @@ var PixodeskAnimator = (() => {
2398
2533
  animatorConfig.mode === PxAnimatorMode.waapi,
2399
2534
  native
2400
2535
  );
2401
- if (api2) return api2;
2536
+ if (api2) {
2537
+ const destroyNative = api2.destroy.bind(api2);
2538
+ api2.destroy = () => {
2539
+ unpin();
2540
+ destroyNative();
2541
+ };
2542
+ return api2;
2543
+ }
2402
2544
  }
2545
+ unpin();
2546
+ unpin = () => {
2547
+ };
2403
2548
  }
2404
2549
  const api = (animatorConfig.mode !== PxAnimatorMode.frames ? createWebApiAnimator(doc, cb, rootElement, animatorConfig.mode === PxAnimatorMode.waapi) : null) || createFrameLoopAnimator(doc, adapter, cb, rootElement);
2405
2550
  const subject = ((_b = api.getRootElement) == null ? void 0 : _b.call(api)) || rootElement;
2406
2551
  if (subject) {
2552
+ unpin = applyScrollPin(subject, animatorConfig.scroll);
2407
2553
  const totalMs = scrollTotalDurationMs(animatorConfig);
2408
2554
  const driver = createScrollDriver(
2409
2555
  subject,
@@ -2414,6 +2560,7 @@ var PixodeskAnimator = (() => {
2414
2560
  const destroy = api.destroy.bind(api);
2415
2561
  api.destroy = () => {
2416
2562
  driver.destroy();
2563
+ unpin();
2417
2564
  destroy();
2418
2565
  };
2419
2566
  }