@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.
package/dist/index.cjs CHANGED
@@ -50,6 +50,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
50
50
  var index_exports = {};
51
51
  __export(index_exports, {
52
52
  COLOUR_ATTR_NAMES: () => COLOUR_ATTR_NAMES,
53
+ MISSING_GLYPH_CLASS_NAME: () => MISSING_GLYPH_CLASS_NAME,
53
54
  PX_ANIMATOR_DATA_KEY: () => PX_ANIMATOR_DATA_KEY,
54
55
  PX_ANIM_ATTR_NAME: () => PX_ANIM_ATTR_NAME,
55
56
  PX_ANIM_SRC_ATTR_NAME: () => PX_ANIM_SRC_ATTR_NAME,
@@ -1316,6 +1317,13 @@ var PxScrollSchema = implementsInterface()(px.object({
1316
1317
  kind: px.enum(["view", "scroll"]).optional(),
1317
1318
  axis: px.enum(["block", "inline", "x", "y"]).optional(),
1318
1319
  source: px.enum(["nearest", "root"]).optional(),
1320
+ // Free-form: the two keywords `parent`/`scroller` plus any CSS selector.
1321
+ subject: px.string().optional(),
1322
+ smoothing: px.number().optional(),
1323
+ pin: px.boolean().optional(),
1324
+ pinAlign: px.enum(["top", "center", "bottom"]).optional(),
1325
+ pinTop: px.number().optional(),
1326
+ pinDistance: px.number().optional(),
1319
1327
  range: PxScrollRangeSchema.optional()
1320
1328
  }));
1321
1329
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
@@ -4548,6 +4556,19 @@ var TEXT_ATTR_KEYS = [
4548
4556
  TEXT_CONTENT_ATTR,
4549
4557
  "xml:space"
4550
4558
  ];
4559
+ var PAINT_STATIC_KEYS = [
4560
+ "fillOpacity",
4561
+ "fillRule",
4562
+ "strokeOpacity",
4563
+ "strokeDasharray",
4564
+ "strokeDashoffset",
4565
+ "strokeLinecap",
4566
+ "strokeLinejoin",
4567
+ "strokeMiterlimit",
4568
+ "mixBlendMode",
4569
+ "filter"
4570
+ ];
4571
+ var PAINT_ANIMATE_KEYS = ["fill", "stroke", "strokeWidth", "opacity", "fillOpacity", "strokeOpacity", "strokeDasharray", "strokeDashoffset"];
4551
4572
  function parseLen(v) {
4552
4573
  if (typeof v === "number") return v;
4553
4574
  if (typeof v === "string") {
@@ -4559,17 +4580,39 @@ function parseLen(v) {
4559
4580
  function str(v) {
4560
4581
  return typeof v === "string" ? v : void 0;
4561
4582
  }
4583
+ function paintAnimateOf(node) {
4584
+ const bag = node.animate;
4585
+ if (!bag || typeof bag !== "object") return void 0;
4586
+ let out;
4587
+ for (const k of PAINT_ANIMATE_KEYS) {
4588
+ if (bag[k] !== void 0) (out != null ? out : out = {})[k] = bag[k];
4589
+ }
4590
+ return out;
4591
+ }
4562
4592
  function resolveStyle2(node, parent) {
4563
- var _a, _b, _c, _d, _e, _f, _g;
4564
- return {
4593
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
4594
+ const isTextRoot = node.type === "text";
4595
+ const nodeStyle = node.style;
4596
+ const own = (key) => {
4597
+ var _a2;
4598
+ return isTextRoot ? void 0 : (_a2 = node[key]) != null ? _a2 : nodeStyle == null ? void 0 : nodeStyle[key];
4599
+ };
4600
+ const res = {
4565
4601
  fontFamily: (_a = str(node.fontFamily)) != null ? _a : parent.fontFamily,
4566
4602
  fontSize: (_b = parseLen(node.fontSize)) != null ? _b : parent.fontSize,
4567
4603
  fill: (_c = node.fill) != null ? _c : parent.fill,
4568
4604
  stroke: (_d = node.stroke) != null ? _d : parent.stroke,
4569
4605
  strokeWidth: (_e = node.strokeWidth) != null ? _e : parent.strokeWidth,
4570
4606
  letterSpacing: (_f = parseLen(node.letterSpacing)) != null ? _f : parent.letterSpacing,
4571
- wordSpacing: (_g = parseLen(node.wordSpacing)) != null ? _g : parent.wordSpacing
4607
+ wordSpacing: (_g = parseLen(node.wordSpacing)) != null ? _g : parent.wordSpacing,
4608
+ opacity: (_h = parseLen(own("opacity"))) != null ? _h : parent.opacity,
4609
+ animate: (_i = isTextRoot ? void 0 : paintAnimateOf(node)) != null ? _i : parent.animate
4572
4610
  };
4611
+ for (const key of PAINT_STATIC_KEYS) {
4612
+ const v = (_j = own(key)) != null ? _j : parent[key];
4613
+ if (v !== void 0) res[key] = v;
4614
+ }
4615
+ return res;
4573
4616
  }
4574
4617
  function rootStyleOf(node) {
4575
4618
  var _a, _b, _c;
@@ -4588,6 +4631,11 @@ function paintOf(s) {
4588
4631
  if (s.fill !== void 0) p.fill = s.fill;
4589
4632
  if (s.stroke !== void 0) p.stroke = s.stroke;
4590
4633
  if (s.strokeWidth !== void 0) p.strokeWidth = s.strokeWidth;
4634
+ if (s.opacity !== void 0 && s.opacity !== 1) p.opacity = s.opacity;
4635
+ for (const key of PAINT_STATIC_KEYS) {
4636
+ if (s[key] !== void 0) p[key] = s[key];
4637
+ }
4638
+ if (s.animate !== void 0) p.animate = s.animate;
4591
4639
  return p;
4592
4640
  }
4593
4641
  function glyphFontFor(s, glyphs, soleFont, warnings) {
@@ -4601,6 +4649,7 @@ function soleFontOf(glyphs) {
4601
4649
  return names.length === 1 ? glyphs[names[0]] : void 0;
4602
4650
  }
4603
4651
  var MISSING_GLYPH_ADVANCE_EM = 0.6;
4652
+ var MISSING_GLYPH_CLASS_NAME = "px-missing-glyph";
4604
4653
  function missingGlyphBoxEm(advanceEm, ascentEm) {
4605
4654
  if (advanceEm <= 0 || ascentEm <= 0) return "";
4606
4655
  const inset = 0.08;
@@ -4636,7 +4685,7 @@ function materialiseGlyphTextHorizontal(node, opts) {
4636
4685
  pen.x += g.width * scale;
4637
4686
  } else if (/\S/.test(ch)) {
4638
4687
  const advEm = g && g.width > 0 ? g.width : MISSING_GLYPH_ADVANCE_EM * upm;
4639
- placements.push({ glyphD: missingGlyphBoxEm(advEm, ascentEm), m: [scale, 0, 0, scale, pen.x, pen.y], paint, line, x: pen.x, y: pen.y, scale });
4688
+ placements.push({ glyphD: missingGlyphBoxEm(advEm, ascentEm), m: [scale, 0, 0, scale, pen.x, pen.y], paint, isMissing: true, line, x: pen.x, y: pen.y, scale });
4640
4689
  pen.x += advEm * scale;
4641
4690
  } else {
4642
4691
  pen.x += (g ? g.width : 0) * scale;
@@ -4827,7 +4876,7 @@ function collectAlongPathCells(node, glyphs, soleFont, warnings) {
4827
4876
  } else if (/\S/.test(ch)) {
4828
4877
  const wEm = g && g.width > 0 ? g.width : MISSING_GLYPH_ADVANCE_EM * gf.unitsPerEm;
4829
4878
  const boxAdv = wEm * scale;
4830
- cells.push({ glyphD: missingGlyphBoxEm(wEm, ascentEm), widthEm: wEm, scale, paint, midBase: adv + boxAdv / 2 });
4879
+ cells.push({ glyphD: missingGlyphBoxEm(wEm, ascentEm), widthEm: wEm, scale, paint, isMissing: true, midBase: adv + boxAdv / 2 });
4831
4880
  adv += boxAdv;
4832
4881
  } else {
4833
4882
  adv += (g ? g.width : 0) * scale;
@@ -4883,6 +4932,7 @@ function materialiseGlyphTextAlongPath(node, pathD, startOffset, opts, textLengt
4883
4932
  const placements = placeCells.map((c) => ({
4884
4933
  glyphD: c.glyphD,
4885
4934
  paint: c.paint,
4935
+ isMissing: c.isMissing,
4886
4936
  m: alongAffine(sampler, base + c.midBase * k, c.scale, c.widthEm, perp)
4887
4937
  }));
4888
4938
  return toGroup(node, buildPaths(placements, create, warnings), create);
@@ -4971,12 +5021,13 @@ function buildAnimatedAlongPath(cells, sampler, distOf, times, loop, create, isC
4971
5021
  const transform = { keyframes: kfs };
4972
5022
  if (loop !== void 0) transform.loop = loop;
4973
5023
  const animate = { transform };
5024
+ if (c.paint.animate) Object.assign(animate, c.paint.animate);
4974
5025
  if (isClip && opKfs.some((k) => k.value === 0)) {
4975
5026
  const op = { keyframes: opKfs };
4976
5027
  if (loop !== void 0) op.loop = loop;
4977
5028
  animate.opacity = op;
4978
5029
  }
4979
- out.push(create("path", __spreadProps2(__spreadValues2({ d }, paintProps(c.paint)), { animate }), []));
5030
+ out.push(create("path", __spreadProps2(__spreadValues2(__spreadValues2({ d }, paintProps(c.paint)), missingGlyphProps(c.isMissing)), { animate }), []));
4980
5031
  }
4981
5032
  return out;
4982
5033
  }
@@ -4985,24 +5036,34 @@ function paintProps(paint) {
4985
5036
  if (paint.fill !== void 0) p.fill = paint.fill;
4986
5037
  if (paint.stroke !== void 0) p.stroke = paint.stroke;
4987
5038
  if (paint.strokeWidth !== void 0) p.strokeWidth = paint.strokeWidth;
5039
+ if (paint.opacity !== void 0) p.opacity = paint.opacity;
5040
+ for (const key of PAINT_STATIC_KEYS) {
5041
+ if (paint[key] !== void 0) p[key] = paint[key];
5042
+ }
4988
5043
  return p;
4989
5044
  }
5045
+ function missingGlyphProps(isMissing) {
5046
+ return isMissing ? { class: MISSING_GLYPH_CLASS_NAME } : {};
5047
+ }
4990
5048
  function buildPaths(placements, create, warnings) {
4991
- var _a, _b, _c;
4992
5049
  if (!placements.length) {
4993
5050
  warnings == null ? void 0 : warnings.push("textGlyphs: nothing to render");
4994
5051
  return [];
4995
5052
  }
4996
5053
  const byPaint = /* @__PURE__ */ new Map();
4997
5054
  for (const p of placements) {
4998
- const key = JSON.stringify([(_a = p.paint.fill) != null ? _a : null, (_b = p.paint.stroke) != null ? _b : null, (_c = p.paint.strokeWidth) != null ? _c : null]);
5055
+ const key = JSON.stringify([p.paint, !!p.isMissing]);
4999
5056
  const baked = transformPathData(p.glyphD, p.m);
5000
5057
  const entry = byPaint.get(key);
5001
5058
  if (entry) entry.d += baked;
5002
- else byPaint.set(key, { paint: p.paint, d: baked });
5059
+ else byPaint.set(key, { paint: p.paint, d: baked, isMissing: p.isMissing });
5003
5060
  }
5004
5061
  const out = [];
5005
- for (const { paint, d } of byPaint.values()) out.push(create("path", __spreadValues2({ d }, paintProps(paint)), []));
5062
+ for (const { paint, d, isMissing } of byPaint.values()) {
5063
+ out.push(create("path", __spreadValues2(__spreadValues2(__spreadValues2({
5064
+ d
5065
+ }, paintProps(paint)), missingGlyphProps(isMissing)), paint.animate !== void 0 ? { animate: __spreadValues2({}, paint.animate) } : {}), []));
5066
+ }
5006
5067
  return out;
5007
5068
  }
5008
5069
  function toGroup(node, children, create) {
@@ -6540,6 +6601,10 @@ function createNativeScrollTimeline(subject, config) {
6540
6601
  if (!config || !isScrollTimeline(config)) return null;
6541
6602
  const scroll = config.scroll || {};
6542
6603
  const kind = (_a = scroll.kind) != null ? _a : "view";
6604
+ if (scroll.smoothing) {
6605
+ console.warn('scroll timeline: `smoothing` needs the built-in driver \u2014 ignoring `driver: "native"`');
6606
+ return null;
6607
+ }
6543
6608
  const g = globalThis;
6544
6609
  const view = kind === "view";
6545
6610
  const Ctor = view ? g.ViewTimeline : g.ScrollTimeline;
@@ -6548,7 +6613,7 @@ function createNativeScrollTimeline(subject, config) {
6548
6613
  let timeline;
6549
6614
  try {
6550
6615
  if (view) {
6551
- timeline = new Ctor({ subject, axis });
6616
+ timeline = new Ctor({ subject: resolveScrollSubject(subject, scroll.subject), axis });
6552
6617
  } else {
6553
6618
  const source = scroll.source === "root" ? documentScroller() : findNearestScroller(subject, "y") || findNearestScroller(subject, "x") || documentScroller();
6554
6619
  timeline = new Ctor({ source, axis });
@@ -6564,7 +6629,10 @@ function createNativeScrollTimeline(subject, config) {
6564
6629
  };
6565
6630
  }
6566
6631
  function findNearestScroller(el, axis) {
6632
+ const body = document.body;
6633
+ const root = document.documentElement;
6567
6634
  for (let p = el.parentElement; p; p = p.parentElement) {
6635
+ if (p === body || p === root) return null;
6568
6636
  const style = getComputedStyle(p);
6569
6637
  const overflow = axis === "y" ? style.overflowY : style.overflowX;
6570
6638
  if (overflow === "auto" || overflow === "scroll" || overflow === "hidden" || overflow === "overlay") {
@@ -6576,11 +6644,42 @@ function findNearestScroller(el, axis) {
6576
6644
  function documentScroller() {
6577
6645
  return document.scrollingElement || document.documentElement;
6578
6646
  }
6647
+ var SUBJECT_PARENT = "parent";
6648
+ var SUBJECT_SCROLLER = "scroller";
6649
+ function resolveScrollSubject(svgRoot, subject) {
6650
+ var _a, _b;
6651
+ const spec = subject == null ? void 0 : subject.trim();
6652
+ if (!spec) return svgRoot;
6653
+ if (spec === SUBJECT_PARENT) {
6654
+ let outermostPinned = null;
6655
+ for (let p = svgRoot.parentElement; p && p !== document.body; p = p.parentElement) {
6656
+ const position = getComputedStyle(p).position;
6657
+ if (position === "sticky" || position === "fixed") outermostPinned = p;
6658
+ }
6659
+ return (_b = (_a = outermostPinned == null ? void 0 : outermostPinned.parentElement) != null ? _a : svgRoot.parentElement) != null ? _b : svgRoot;
6660
+ }
6661
+ if (spec === SUBJECT_SCROLLER) {
6662
+ return findNearestScroller(svgRoot, "y") || findNearestScroller(svgRoot, "x") || documentScroller();
6663
+ }
6664
+ let found = null;
6665
+ try {
6666
+ found = document.querySelector(spec);
6667
+ } catch (e) {
6668
+ console.warn('scroll timeline: subject "' + spec + '" is not a valid selector \u2014 measuring the SVG itself');
6669
+ return svgRoot;
6670
+ }
6671
+ if (!found) {
6672
+ console.warn('scroll timeline: subject "' + spec + '" matched no element \u2014 measuring the SVG itself');
6673
+ return svgRoot;
6674
+ }
6675
+ return found;
6676
+ }
6579
6677
  function createScrollDriver(subject, config, onProgress) {
6580
- var _a;
6678
+ var _a, _b;
6581
6679
  if (!config || !isScrollTimeline(config)) return null;
6582
6680
  const scroll = config.scroll || {};
6583
6681
  const kind = (_a = scroll.kind) != null ? _a : "view";
6682
+ const measured = resolveScrollSubject(subject, scroll.subject);
6584
6683
  const nearest = findNearestScroller(subject, "y") || findNearestScroller(subject, "x");
6585
6684
  const scroller = kind === "scroll" && scroll.source === "root" ? documentScroller() : nearest || documentScroller();
6586
6685
  const isRootScroller = scroller === documentScroller();
@@ -6591,7 +6690,7 @@ function createScrollDriver(subject, config, onProgress) {
6591
6690
  const maxOffset = axis === "y" ? scroller.scrollHeight - scroller.clientHeight : scroller.scrollWidth - scroller.clientWidth;
6592
6691
  return scrollOffsetProgress(offset, maxOffset, scroll.range);
6593
6692
  }
6594
- const subjectRect = subject.getBoundingClientRect();
6693
+ const subjectRect = measured.getBoundingClientRect();
6595
6694
  let portStart, portSize;
6596
6695
  if (isRootScroller) {
6597
6696
  portStart = 0;
@@ -6605,12 +6704,43 @@ function createScrollDriver(subject, config, onProgress) {
6605
6704
  const subjectSize = axis === "y" ? subjectRect.height : subjectRect.width;
6606
6705
  return scrollViewProgress(subjectStart, subjectSize, portSize, scroll.range);
6607
6706
  };
6608
- let rafId = null;
6707
+ const smoothingSec = Math.max(0, (_b = scroll.smoothing) != null ? _b : 0) / 1e3;
6708
+ const SETTLE_EPSILON = 1e-3;
6609
6709
  let destroyed = false;
6710
+ let smoothed = null;
6711
+ let smoothRaf = null;
6712
+ let lastFrameMs = 0;
6713
+ const emit = (target) => {
6714
+ if (!smoothingSec) {
6715
+ onProgress(target);
6716
+ return;
6717
+ }
6718
+ if (smoothed === null) {
6719
+ smoothed = target;
6720
+ onProgress(target);
6721
+ return;
6722
+ }
6723
+ if (smoothRaf !== null) return;
6724
+ lastFrameMs = 0;
6725
+ const step = (nowMs) => {
6726
+ smoothRaf = null;
6727
+ if (destroyed) return;
6728
+ const dtSec = lastFrameMs ? Math.min(0.1, (nowMs - lastFrameMs) / 1e3) : 1 / 60;
6729
+ lastFrameMs = nowMs;
6730
+ const goal = compute();
6731
+ const k = 1 - Math.exp(-dtSec / smoothingSec);
6732
+ smoothed = smoothed + (goal - smoothed) * k;
6733
+ if (Math.abs(goal - smoothed) < SETTLE_EPSILON) smoothed = goal;
6734
+ onProgress(smoothed);
6735
+ if (smoothed !== goal) smoothRaf = requestAnimationFrame(step);
6736
+ };
6737
+ smoothRaf = requestAnimationFrame(step);
6738
+ };
6739
+ let rafId = null;
6610
6740
  const tick = () => {
6611
6741
  rafId = null;
6612
6742
  if (destroyed) return;
6613
- onProgress(compute());
6743
+ emit(compute());
6614
6744
  };
6615
6745
  const schedule = () => {
6616
6746
  if (destroyed || rafId !== null) return;
@@ -6622,7 +6752,8 @@ function createScrollDriver(subject, config, onProgress) {
6622
6752
  let resizeObserver;
6623
6753
  if (typeof ResizeObserver !== "undefined") {
6624
6754
  resizeObserver = new ResizeObserver(schedule);
6625
- resizeObserver.observe(subject);
6755
+ resizeObserver.observe(measured);
6756
+ if (measured !== subject) resizeObserver.observe(subject);
6626
6757
  if (!isRootScroller) resizeObserver.observe(scroller);
6627
6758
  }
6628
6759
  const driver = {
@@ -6636,14 +6767,76 @@ function createScrollDriver(subject, config, onProgress) {
6636
6767
  cancelAnimationFrame(rafId);
6637
6768
  rafId = null;
6638
6769
  }
6770
+ if (smoothRaf !== null) {
6771
+ cancelAnimationFrame(smoothRaf);
6772
+ smoothRaf = null;
6773
+ }
6639
6774
  },
6775
+ // `refresh` is a deliberate JUMP (attach, host relayout) — never eased.
6640
6776
  refresh: () => {
6641
- if (!destroyed) onProgress(compute());
6777
+ if (!destroyed) {
6778
+ smoothed = compute();
6779
+ onProgress(smoothed);
6780
+ }
6642
6781
  }
6643
6782
  };
6644
6783
  driver.refresh();
6645
6784
  return driver;
6646
6785
  }
6786
+ function pinScrollportHeight(svgRoot) {
6787
+ const scroller = findNearestScroller(svgRoot, "y");
6788
+ return scroller ? scroller.clientHeight : document.documentElement.clientHeight;
6789
+ }
6790
+ function applyScrollPin(svgRoot, scroll) {
6791
+ const styled = svgRoot;
6792
+ if (!(scroll == null ? void 0 : scroll.pin) || !styled.style) return () => {
6793
+ };
6794
+ const style = styled.style;
6795
+ const prevPosition = style.position;
6796
+ const prevTop = style.top;
6797
+ style.position = "sticky";
6798
+ const alignFactor = scroll.pinAlign === "center" ? 0.5 : scroll.pinAlign === "bottom" ? 1 : 0;
6799
+ const applyTop = () => {
6800
+ var _a;
6801
+ const extra = (_a = scroll.pinTop) != null ? _a : 0;
6802
+ if (!alignFactor) {
6803
+ style.top = extra + "px";
6804
+ return;
6805
+ }
6806
+ const portSize = pinScrollportHeight(svgRoot);
6807
+ const ownSize = svgRoot.getBoundingClientRect().height;
6808
+ style.top = Math.round((portSize - ownSize) * alignFactor + extra) + "px";
6809
+ };
6810
+ applyTop();
6811
+ let resizeObserver = null;
6812
+ const onWindowResize = alignFactor ? applyTop : null;
6813
+ if (alignFactor) {
6814
+ if (onWindowResize) window.addEventListener("resize", onWindowResize);
6815
+ if (typeof ResizeObserver !== "undefined") {
6816
+ resizeObserver = new ResizeObserver(applyTop);
6817
+ resizeObserver.observe(svgRoot);
6818
+ }
6819
+ }
6820
+ let wrapper = null;
6821
+ const parent = svgRoot.parentElement;
6822
+ if (scroll.pinDistance && scroll.pinDistance > 0 && parent) {
6823
+ wrapper = document.createElement("div");
6824
+ wrapper.setAttribute("data-px-pin", "");
6825
+ wrapper.style.height = scroll.pinDistance * 100 + "vh";
6826
+ parent.insertBefore(wrapper, svgRoot);
6827
+ wrapper.appendChild(svgRoot);
6828
+ }
6829
+ return () => {
6830
+ if (onWindowResize) window.removeEventListener("resize", onWindowResize);
6831
+ resizeObserver == null ? void 0 : resizeObserver.disconnect();
6832
+ style.position = prevPosition;
6833
+ style.top = prevTop;
6834
+ if (wrapper == null ? void 0 : wrapper.parentElement) {
6835
+ wrapper.parentElement.insertBefore(svgRoot, wrapper);
6836
+ wrapper.remove();
6837
+ }
6838
+ };
6839
+ }
6647
6840
 
6648
6841
  // src/PxAnimatorBind.ts
6649
6842
  function finaliseAnimator(animatorConfig, callbacks, make) {
@@ -6670,7 +6863,10 @@ function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
6670
6863
  if (isScrollTimeline(animatorConfig)) {
6671
6864
  return finaliseAnimator(animatorConfig, callbacks, (cb) => {
6672
6865
  var _a, _b;
6866
+ let unpin = () => {
6867
+ };
6673
6868
  if (animatorConfig.mode !== PxAnimatorMode.frames && ((_a = animatorConfig.scroll) == null ? void 0 : _a.driver) === "native" && rootElement) {
6869
+ unpin = applyScrollPin(rootElement, animatorConfig.scroll);
6674
6870
  const native = createNativeScrollTimeline(rootElement, animatorConfig);
6675
6871
  if (native) {
6676
6872
  const api2 = createWebApiAnimator(
@@ -6680,12 +6876,23 @@ function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
6680
6876
  animatorConfig.mode === PxAnimatorMode.waapi,
6681
6877
  native
6682
6878
  );
6683
- if (api2) return api2;
6879
+ if (api2) {
6880
+ const destroyNative = api2.destroy.bind(api2);
6881
+ api2.destroy = () => {
6882
+ unpin();
6883
+ destroyNative();
6884
+ };
6885
+ return api2;
6886
+ }
6684
6887
  }
6888
+ unpin();
6889
+ unpin = () => {
6890
+ };
6685
6891
  }
6686
6892
  const api = (animatorConfig.mode !== PxAnimatorMode.frames ? createWebApiAnimator(doc, cb, rootElement, animatorConfig.mode === PxAnimatorMode.waapi) : null) || createFrameLoopAnimator(doc, adapter, cb, rootElement);
6687
6893
  const subject = ((_b = api.getRootElement) == null ? void 0 : _b.call(api)) || rootElement;
6688
6894
  if (subject) {
6895
+ unpin = applyScrollPin(subject, animatorConfig.scroll);
6689
6896
  const totalMs = scrollTotalDurationMs(animatorConfig);
6690
6897
  const driver = createScrollDriver(
6691
6898
  subject,
@@ -6696,6 +6903,7 @@ function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
6696
6903
  const destroy = api.destroy.bind(api);
6697
6904
  api.destroy = () => {
6698
6905
  driver.destroy();
6906
+ unpin();
6699
6907
  destroy();
6700
6908
  };
6701
6909
  }
@@ -6888,6 +7096,7 @@ if (typeof window !== "undefined") {
6888
7096
  // Annotate the CommonJS export names for ESM import in node:
6889
7097
  0 && (module.exports = {
6890
7098
  COLOUR_ATTR_NAMES,
7099
+ MISSING_GLYPH_CLASS_NAME,
6891
7100
  PX_ANIMATOR_DATA_KEY,
6892
7101
  PX_ANIM_ATTR_NAME,
6893
7102
  PX_ANIM_SRC_ATTR_NAME,