@pixodesk/svg-animator-vue 1.0.27 → 1.0.28

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
@@ -57939,6 +57939,12 @@ ${codeFrame}` : message);
57939
57939
  kind: px.enum(["view", "scroll"]).optional(),
57940
57940
  axis: px.enum(["block", "inline", "x", "y"]).optional(),
57941
57941
  source: px.enum(["nearest", "root"]).optional(),
57942
+ // Free-form: the two keywords `parent`/`scroller` plus any CSS selector.
57943
+ subject: px.string().optional(),
57944
+ smoothing: px.number().optional(),
57945
+ pin: px.boolean().optional(),
57946
+ pinTop: px.number().optional(),
57947
+ pinDistance: px.number().optional(),
57942
57948
  range: PxScrollRangeSchema.optional()
57943
57949
  }));
57944
57950
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
@@ -62812,6 +62818,10 @@ ${codeFrame}` : message);
62812
62818
  if (!config || !isScrollTimeline(config)) return null;
62813
62819
  const scroll = config.scroll || {};
62814
62820
  const kind = (_a = scroll.kind) != null ? _a : "view";
62821
+ if (scroll.smoothing) {
62822
+ console.warn('scroll timeline: `smoothing` needs the built-in driver \u2014 ignoring `driver: "native"`');
62823
+ return null;
62824
+ }
62815
62825
  const g = globalThis;
62816
62826
  const view = kind === "view";
62817
62827
  const Ctor = view ? g.ViewTimeline : g.ScrollTimeline;
@@ -62820,7 +62830,7 @@ ${codeFrame}` : message);
62820
62830
  let timeline;
62821
62831
  try {
62822
62832
  if (view) {
62823
- timeline = new Ctor({ subject, axis });
62833
+ timeline = new Ctor({ subject: resolveScrollSubject(subject, scroll.subject), axis });
62824
62834
  } else {
62825
62835
  const source = scroll.source === "root" ? documentScroller() : findNearestScroller(subject, "y") || findNearestScroller(subject, "x") || documentScroller();
62826
62836
  timeline = new Ctor({ source, axis });
@@ -62836,7 +62846,10 @@ ${codeFrame}` : message);
62836
62846
  };
62837
62847
  }
62838
62848
  function findNearestScroller(el, axis) {
62849
+ const body = document.body;
62850
+ const root = document.documentElement;
62839
62851
  for (let p = el.parentElement; p; p = p.parentElement) {
62852
+ if (p === body || p === root) return null;
62840
62853
  const style = getComputedStyle(p);
62841
62854
  const overflow = axis === "y" ? style.overflowY : style.overflowX;
62842
62855
  if (overflow === "auto" || overflow === "scroll" || overflow === "hidden" || overflow === "overlay") {
@@ -62848,11 +62861,42 @@ ${codeFrame}` : message);
62848
62861
  function documentScroller() {
62849
62862
  return document.scrollingElement || document.documentElement;
62850
62863
  }
62864
+ var SUBJECT_PARENT = "parent";
62865
+ var SUBJECT_SCROLLER = "scroller";
62866
+ function resolveScrollSubject(svgRoot, subject) {
62867
+ var _a, _b;
62868
+ const spec = subject == null ? void 0 : subject.trim();
62869
+ if (!spec) return svgRoot;
62870
+ if (spec === SUBJECT_PARENT) {
62871
+ let outermostPinned = null;
62872
+ for (let p = svgRoot.parentElement; p && p !== document.body; p = p.parentElement) {
62873
+ const position = getComputedStyle(p).position;
62874
+ if (position === "sticky" || position === "fixed") outermostPinned = p;
62875
+ }
62876
+ return (_b = (_a = outermostPinned == null ? void 0 : outermostPinned.parentElement) != null ? _a : svgRoot.parentElement) != null ? _b : svgRoot;
62877
+ }
62878
+ if (spec === SUBJECT_SCROLLER) {
62879
+ return findNearestScroller(svgRoot, "y") || findNearestScroller(svgRoot, "x") || documentScroller();
62880
+ }
62881
+ let found = null;
62882
+ try {
62883
+ found = document.querySelector(spec);
62884
+ } catch (e) {
62885
+ console.warn('scroll timeline: subject "' + spec + '" is not a valid selector \u2014 measuring the SVG itself');
62886
+ return svgRoot;
62887
+ }
62888
+ if (!found) {
62889
+ console.warn('scroll timeline: subject "' + spec + '" matched no element \u2014 measuring the SVG itself');
62890
+ return svgRoot;
62891
+ }
62892
+ return found;
62893
+ }
62851
62894
  function createScrollDriver(subject, config, onProgress) {
62852
- var _a;
62895
+ var _a, _b;
62853
62896
  if (!config || !isScrollTimeline(config)) return null;
62854
62897
  const scroll = config.scroll || {};
62855
62898
  const kind = (_a = scroll.kind) != null ? _a : "view";
62899
+ const measured = resolveScrollSubject(subject, scroll.subject);
62856
62900
  const nearest = findNearestScroller(subject, "y") || findNearestScroller(subject, "x");
62857
62901
  const scroller = kind === "scroll" && scroll.source === "root" ? documentScroller() : nearest || documentScroller();
62858
62902
  const isRootScroller = scroller === documentScroller();
@@ -62863,7 +62907,7 @@ ${codeFrame}` : message);
62863
62907
  const maxOffset = axis === "y" ? scroller.scrollHeight - scroller.clientHeight : scroller.scrollWidth - scroller.clientWidth;
62864
62908
  return scrollOffsetProgress(offset, maxOffset, scroll.range);
62865
62909
  }
62866
- const subjectRect = subject.getBoundingClientRect();
62910
+ const subjectRect = measured.getBoundingClientRect();
62867
62911
  let portStart, portSize;
62868
62912
  if (isRootScroller) {
62869
62913
  portStart = 0;
@@ -62877,12 +62921,43 @@ ${codeFrame}` : message);
62877
62921
  const subjectSize = axis === "y" ? subjectRect.height : subjectRect.width;
62878
62922
  return scrollViewProgress(subjectStart, subjectSize, portSize, scroll.range);
62879
62923
  };
62880
- let rafId = null;
62924
+ const smoothingSec = Math.max(0, (_b = scroll.smoothing) != null ? _b : 0) / 1e3;
62925
+ const SETTLE_EPSILON = 1e-3;
62881
62926
  let destroyed = false;
62927
+ let smoothed = null;
62928
+ let smoothRaf = null;
62929
+ let lastFrameMs = 0;
62930
+ const emit = (target) => {
62931
+ if (!smoothingSec) {
62932
+ onProgress(target);
62933
+ return;
62934
+ }
62935
+ if (smoothed === null) {
62936
+ smoothed = target;
62937
+ onProgress(target);
62938
+ return;
62939
+ }
62940
+ if (smoothRaf !== null) return;
62941
+ lastFrameMs = 0;
62942
+ const step = (nowMs) => {
62943
+ smoothRaf = null;
62944
+ if (destroyed) return;
62945
+ const dtSec = lastFrameMs ? Math.min(0.1, (nowMs - lastFrameMs) / 1e3) : 1 / 60;
62946
+ lastFrameMs = nowMs;
62947
+ const goal = compute();
62948
+ const k = 1 - Math.exp(-dtSec / smoothingSec);
62949
+ smoothed = smoothed + (goal - smoothed) * k;
62950
+ if (Math.abs(goal - smoothed) < SETTLE_EPSILON) smoothed = goal;
62951
+ onProgress(smoothed);
62952
+ if (smoothed !== goal) smoothRaf = requestAnimationFrame(step);
62953
+ };
62954
+ smoothRaf = requestAnimationFrame(step);
62955
+ };
62956
+ let rafId = null;
62882
62957
  const tick = () => {
62883
62958
  rafId = null;
62884
62959
  if (destroyed) return;
62885
- onProgress(compute());
62960
+ emit(compute());
62886
62961
  };
62887
62962
  const schedule = () => {
62888
62963
  if (destroyed || rafId !== null) return;
@@ -62894,7 +62969,8 @@ ${codeFrame}` : message);
62894
62969
  let resizeObserver;
62895
62970
  if (typeof ResizeObserver !== "undefined") {
62896
62971
  resizeObserver = new ResizeObserver(schedule);
62897
- resizeObserver.observe(subject);
62972
+ resizeObserver.observe(measured);
62973
+ if (measured !== subject) resizeObserver.observe(subject);
62898
62974
  if (!isRootScroller) resizeObserver.observe(scroller);
62899
62975
  }
62900
62976
  const driver = {
@@ -62908,14 +62984,50 @@ ${codeFrame}` : message);
62908
62984
  cancelAnimationFrame(rafId);
62909
62985
  rafId = null;
62910
62986
  }
62987
+ if (smoothRaf !== null) {
62988
+ cancelAnimationFrame(smoothRaf);
62989
+ smoothRaf = null;
62990
+ }
62911
62991
  },
62992
+ // `refresh` is a deliberate JUMP (attach, host relayout) — never eased.
62912
62993
  refresh: () => {
62913
- if (!destroyed) onProgress(compute());
62994
+ if (!destroyed) {
62995
+ smoothed = compute();
62996
+ onProgress(smoothed);
62997
+ }
62914
62998
  }
62915
62999
  };
62916
63000
  driver.refresh();
62917
63001
  return driver;
62918
63002
  }
63003
+ function applyScrollPin(svgRoot, scroll) {
63004
+ var _a;
63005
+ const styled = svgRoot;
63006
+ if (!(scroll == null ? void 0 : scroll.pin) || !styled.style) return () => {
63007
+ };
63008
+ const style = styled.style;
63009
+ const prevPosition = style.position;
63010
+ const prevTop = style.top;
63011
+ style.position = "sticky";
63012
+ style.top = ((_a = scroll.pinTop) != null ? _a : 0) + "px";
63013
+ let wrapper = null;
63014
+ const parent = svgRoot.parentElement;
63015
+ if (scroll.pinDistance && scroll.pinDistance > 0 && parent) {
63016
+ wrapper = document.createElement("div");
63017
+ wrapper.setAttribute("data-px-pin", "");
63018
+ wrapper.style.height = scroll.pinDistance * 100 + "vh";
63019
+ parent.insertBefore(wrapper, svgRoot);
63020
+ wrapper.appendChild(svgRoot);
63021
+ }
63022
+ return () => {
63023
+ style.position = prevPosition;
63024
+ style.top = prevTop;
63025
+ if (wrapper == null ? void 0 : wrapper.parentElement) {
63026
+ wrapper.parentElement.insertBefore(svgRoot, wrapper);
63027
+ wrapper.remove();
63028
+ }
63029
+ };
63030
+ }
62919
63031
  function finaliseAnimator(animatorConfig, callbacks, make) {
62920
63032
  let apiRef;
62921
63033
  let effectiveCallbacks = callbacks;
@@ -62940,7 +63052,10 @@ ${codeFrame}` : message);
62940
63052
  if (isScrollTimeline(animatorConfig)) {
62941
63053
  return finaliseAnimator(animatorConfig, callbacks, (cb) => {
62942
63054
  var _a, _b;
63055
+ let unpin = () => {
63056
+ };
62943
63057
  if (animatorConfig.mode !== PxAnimatorMode.frames && ((_a = animatorConfig.scroll) == null ? void 0 : _a.driver) === "native" && rootElement) {
63058
+ unpin = applyScrollPin(rootElement, animatorConfig.scroll);
62944
63059
  const native = createNativeScrollTimeline(rootElement, animatorConfig);
62945
63060
  if (native) {
62946
63061
  const api2 = createWebApiAnimator(
@@ -62950,12 +63065,23 @@ ${codeFrame}` : message);
62950
63065
  animatorConfig.mode === PxAnimatorMode.waapi,
62951
63066
  native
62952
63067
  );
62953
- if (api2) return api2;
63068
+ if (api2) {
63069
+ const destroyNative = api2.destroy.bind(api2);
63070
+ api2.destroy = () => {
63071
+ unpin();
63072
+ destroyNative();
63073
+ };
63074
+ return api2;
63075
+ }
62954
63076
  }
63077
+ unpin();
63078
+ unpin = () => {
63079
+ };
62955
63080
  }
62956
63081
  const api = (animatorConfig.mode !== PxAnimatorMode.frames ? createWebApiAnimator(doc, cb, rootElement, animatorConfig.mode === PxAnimatorMode.waapi) : null) || createFrameLoopAnimator(doc, adapter, cb, rootElement);
62957
63082
  const subject = ((_b = api.getRootElement) == null ? void 0 : _b.call(api)) || rootElement;
62958
63083
  if (subject) {
63084
+ unpin = applyScrollPin(subject, animatorConfig.scroll);
62959
63085
  const totalMs = scrollTotalDurationMs(animatorConfig);
62960
63086
  const driver = createScrollDriver(
62961
63087
  subject,
@@ -62966,6 +63092,7 @@ ${codeFrame}` : message);
62966
63092
  const destroy = api.destroy.bind(api);
62967
63093
  api.destroy = () => {
62968
63094
  driver.destroy();
63095
+ unpin();
62969
63096
  destroy();
62970
63097
  };
62971
63098
  }