svg-scroll-draw 2.23.1 → 2.23.4

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.
Files changed (65) hide show
  1. package/README.md +18 -19
  2. package/dist/angular/index.cjs +3 -3
  3. package/dist/angular/index.mjs +1 -1
  4. package/dist/astro/index.cjs +2 -2
  5. package/dist/astro/index.mjs +1 -1
  6. package/dist/cdn/svg-scroll-draw.dev.global.js +140 -176
  7. package/dist/cdn/svg-scroll-draw.global.js +3 -3
  8. package/dist/chunk-6TWFLRUB.mjs +3 -0
  9. package/dist/{chunk-OTDD2ROH.mjs → chunk-DSFD227B.mjs} +1 -1
  10. package/dist/chunk-I73SOXBN.mjs +1 -0
  11. package/dist/chunk-NLPSVYFP.mjs +1 -0
  12. package/dist/chunk-OPXWNJBG.mjs +1 -0
  13. package/dist/chunk-QFSTKVA3.mjs +1 -0
  14. package/dist/chunk-UPWWULKI.mjs +1 -0
  15. package/dist/chunk-XC5NR2MU.mjs +1 -0
  16. package/dist/{chunk-GVBO5GRD.mjs → chunk-XTPAW6XR.mjs} +1 -1
  17. package/dist/{chunk-VMUJMXKO.mjs → chunk-ZNCZYV3Q.mjs} +1 -1
  18. package/dist/group/index.cjs +2 -2
  19. package/dist/group/index.d.mts +4 -2
  20. package/dist/group/index.d.ts +4 -2
  21. package/dist/group/index.mjs +1 -1
  22. package/dist/horizontal/index.cjs +1 -1
  23. package/dist/horizontal/index.d.mts +10 -7
  24. package/dist/horizontal/index.d.ts +10 -7
  25. package/dist/horizontal/index.mjs +1 -1
  26. package/dist/index.cjs +4 -4
  27. package/dist/index.d.mts +3 -2
  28. package/dist/index.d.ts +3 -2
  29. package/dist/index.mjs +1 -1
  30. package/dist/nuxt/index.cjs +3 -3
  31. package/dist/nuxt/index.mjs +1 -1
  32. package/dist/progress/index.cjs +1 -1
  33. package/dist/progress/index.mjs +1 -1
  34. package/dist/react/index.cjs +3 -3
  35. package/dist/react/index.d.mts +3 -1
  36. package/dist/react/index.d.ts +3 -1
  37. package/dist/react/index.mjs +1 -1
  38. package/dist/reveal/index.cjs +1 -1
  39. package/dist/reveal/index.d.mts +9 -3
  40. package/dist/reveal/index.d.ts +9 -3
  41. package/dist/reveal/index.mjs +1 -1
  42. package/dist/snap/index.cjs +1 -1
  43. package/dist/snap/index.mjs +1 -1
  44. package/dist/solid/index.cjs +3 -3
  45. package/dist/solid/index.mjs +1 -1
  46. package/dist/svelte/index.cjs +3 -3
  47. package/dist/svelte/index.mjs +1 -1
  48. package/dist/text/index.cjs +1 -1
  49. package/dist/text/index.mjs +1 -1
  50. package/dist/timeline/index.cjs +1 -1
  51. package/dist/timeline/index.mjs +1 -1
  52. package/dist/video/index.cjs +1 -1
  53. package/dist/video/index.mjs +1 -1
  54. package/dist/vue/index.cjs +3 -3
  55. package/dist/vue/index.mjs +1 -1
  56. package/dist/web-component/index.cjs +3 -3
  57. package/dist/web-component/index.mjs +1 -1
  58. package/package.json +1 -1
  59. package/dist/chunk-DY5JCXVR.mjs +0 -1
  60. package/dist/chunk-HX4SP5WN.mjs +0 -3
  61. package/dist/chunk-KV3WCPQ5.mjs +0 -1
  62. package/dist/chunk-LTCJVVSC.mjs +0 -1
  63. package/dist/chunk-WXUOF4NM.mjs +0 -1
  64. package/dist/chunk-YWKPTYSG.mjs +0 -1
  65. package/dist/chunk-YZV4Z3EQ.mjs +0 -1
@@ -150,7 +150,7 @@ var SvgScrollDraw = (() => {
150
150
  const rect = el.getBoundingClientRect();
151
151
  const size = axis === "x" ? rect.width : rect.height;
152
152
  if (scrollEl) {
153
- const top = axis === "x" ? rect.left - scrollEl.getBoundingClientRect().left + scrollEl.scrollLeft : rect.top - scrollEl.getBoundingClientRect().top + scrollEl.scrollTop;
153
+ const top = axis === "x" ? rect.left - scrollEl.getBoundingClientRect().left - scrollEl.clientLeft + scrollEl.scrollLeft : rect.top - scrollEl.getBoundingClientRect().top - scrollEl.clientTop + scrollEl.scrollTop;
154
154
  return { top, height: size, scroll: 0 };
155
155
  }
156
156
  return {
@@ -162,6 +162,8 @@ var SvgScrollDraw = (() => {
162
162
 
163
163
  // src/core/geometry.ts
164
164
  function getElementLength(el) {
165
+ const declared = parseFloat(el.getAttribute("pathLength") ?? "");
166
+ if (declared > 0) return declared;
165
167
  const tag = el.tagName.toLowerCase();
166
168
  if (tag === "rect") {
167
169
  const w = parseFloat(el.getAttribute("width") ?? "0");
@@ -536,8 +538,8 @@ var SvgScrollDraw = (() => {
536
538
  } = options;
537
539
  const clipDirection = clip === true ? "left" : typeof clip === "string" ? clip : false;
538
540
  const easeFn = typeof easing === "function" ? easing : EASINGS[easing] ?? EASINGS.linear;
539
- const startConfig = parseTrigger(trigger.start ?? "top bottom");
540
- const endConfig = parseTrigger(trigger.end ?? "bottom top");
541
+ const startConfig = parseTrigger(trigger.start || "top bottom");
542
+ const endConfig = parseTrigger(trigger.end || "bottom top");
541
543
  const scrollEl = typeof scrollContainer === "string" ? document.querySelector(scrollContainer) : scrollContainer ?? null;
542
544
  const colorFrom = Array.isArray(strokeColor) ? strokeColor[0] : null;
543
545
  const colorTo = Array.isArray(strokeColor) ? strokeColor[1] : typeof strokeColor === "string" ? strokeColor : null;
@@ -630,6 +632,9 @@ var SvgScrollDraw = (() => {
630
632
  };
631
633
  return inst;
632
634
  }
635
+ function latch(i, raw) {
636
+ return once && !autoReverse ? Math.max(frozenAlpha, easeFn(frozenPath[i] = Math.max(frozenPath[i] ?? 0, raw))) : easeFn(raw);
637
+ }
633
638
  function applyAlpha(alpha, dir) {
634
639
  container.style.setProperty("--scroll-draw-progress", String(alpha));
635
640
  if (clipDirection) {
@@ -637,28 +642,28 @@ var SvgScrollDraw = (() => {
637
642
  container.style.clipPath = computeClipPath(a);
638
643
  return;
639
644
  }
640
- paths.forEach((el, i) => {
641
- el.style.strokeDashoffset = dir === "reverse" ? `${lengths[i] * alpha}` : `${lengths[i] * (1 - alpha)}`;
642
- if (fade) el.style.opacity = dir === "reverse" ? `${1 - alpha}` : `${alpha}`;
643
- if (colorFrom && colorTo) el.style.stroke = lerpColor(colorFrom, colorTo, alpha);
644
- else if (colorTo) el.style.stroke = colorTo;
645
- if (widthFrom !== null && widthTo !== null)
646
- el.style.strokeWidth = `${widthFrom + (widthTo - widthFrom) * alpha}`;
647
- else if (widthTo !== null)
648
- el.style.strokeWidth = `${widthTo}`;
649
- if (fillOpacityFrom !== null && fillOpacityTo !== null)
650
- el.style.fillOpacity = `${fillOpacityFrom + (fillOpacityTo - fillOpacityFrom) * alpha}`;
651
- else if (fillOpacityTo !== null)
652
- el.style.fillOpacity = `${fillOpacityTo}`;
653
- if (morphTo && el.tagName.toLowerCase() === "path" && originalDs[i]) {
654
- el.setAttribute("d", morphPath(originalDs[i], morphTo, alpha));
655
- }
656
- });
645
+ paths.forEach((el, i) => paint(el, i, alpha, dir));
646
+ }
647
+ function paint(el, i, alpha, dir) {
648
+ el.style.strokeDashoffset = dir === "reverse" ? `${lengths[i] * alpha}` : `${lengths[i] * (1 - alpha)}`;
649
+ if (fade) el.style.opacity = dir === "reverse" ? `${1 - alpha}` : `${alpha}`;
650
+ if (colorFrom && colorTo) el.style.stroke = lerpColor(colorFrom, colorTo, alpha);
651
+ else if (colorTo) el.style.stroke = colorTo;
652
+ if (widthFrom !== null && widthTo !== null)
653
+ el.style.strokeWidth = `${widthFrom + (widthTo - widthFrom) * alpha}`;
654
+ else if (widthTo !== null)
655
+ el.style.strokeWidth = `${widthTo}`;
656
+ if (fillOpacityFrom !== null && fillOpacityTo !== null)
657
+ el.style.fillOpacity = `${fillOpacityFrom + (fillOpacityTo - fillOpacityFrom) * alpha}`;
658
+ else if (fillOpacityTo !== null)
659
+ el.style.fillOpacity = `${fillOpacityTo}`;
660
+ if (morphTo && el.tagName.toLowerCase() === "path" && originalDs[i])
661
+ el.setAttribute("d", morphPath(originalDs[i], morphTo, alpha));
657
662
  }
658
663
  function resetPaths() {
659
664
  container.style.setProperty("--scroll-draw-progress", "0");
660
665
  if (clipDirection) {
661
- container.style.clipPath = computeClipPath(0);
666
+ container.style.clipPath = computeClipPath(direction === "reverse" ? 1 : 0);
662
667
  return;
663
668
  }
664
669
  paths.forEach((el, i) => {
@@ -673,13 +678,26 @@ var SvgScrollDraw = (() => {
673
678
  el.setAttribute("d", originalDs[i]);
674
679
  });
675
680
  }
676
- paths.forEach((el) => {
681
+ function measure(el, i) {
682
+ const d = el.getAttribute("d");
683
+ let len = getElementLength(el);
684
+ if (morphTo && originalDs[i]) {
685
+ el.setAttribute("d", morphTo);
686
+ const target = getElementLength(el);
687
+ el.setAttribute("d", originalDs[i]);
688
+ len = Math.max(getElementLength(el), target);
689
+ el.setAttribute("d", d);
690
+ }
691
+ lengths[i] = len;
692
+ el.style.strokeDasharray = `${len}`;
693
+ }
694
+ paths.forEach((el, i) => {
677
695
  checkElement(el);
678
- const len = getElementLength(el);
679
- lengths.push(len);
680
696
  const isPath = el.tagName.toLowerCase() === "path";
681
697
  if (isPath) originalDs.push(el.getAttribute("d") ?? "");
682
698
  else originalDs.push("");
699
+ measure(el, i);
700
+ const len = lengths[i];
683
701
  if (morphTo) {
684
702
  if (!isPath) {
685
703
  warn(`morphTo only applies to <path> elements \u2014 ignored on <${el.tagName}>.`, el);
@@ -688,7 +706,6 @@ var SvgScrollDraw = (() => {
688
706
  }
689
707
  }
690
708
  if (prefersReduced) {
691
- el.style.strokeDasharray = `${len}`;
692
709
  el.style.strokeDashoffset = direction === "reverse" ? `${len}` : "0";
693
710
  if (fade) el.style.opacity = "1";
694
711
  if (colorTo) el.style.stroke = colorTo;
@@ -696,7 +713,6 @@ var SvgScrollDraw = (() => {
696
713
  if (fillOpacityTo !== null) el.style.fillOpacity = `${fillOpacityTo}`;
697
714
  if (morphTo && el.tagName.toLowerCase() === "path") el.setAttribute("d", morphTo);
698
715
  } else {
699
- el.style.strokeDasharray = `${len}`;
700
716
  el.style.strokeDashoffset = direction === "reverse" ? "0" : `${len}`;
701
717
  if (fade) el.style.opacity = direction === "reverse" ? "1" : "0";
702
718
  else el.style.opacity = "";
@@ -705,19 +721,8 @@ var SvgScrollDraw = (() => {
705
721
  if (fillOpacityFrom !== null) el.style.fillOpacity = `${fillOpacityFrom}`;
706
722
  }
707
723
  });
708
- if (clipDirection) {
709
- if (prefersReduced) {
710
- container.style.clipPath = computeClipPath(1);
711
- onComplete?.();
712
- return { destroy: () => {
713
- }, replay: () => {
714
- }, pause: () => {
715
- }, resume: () => {
716
- }, seek: () => {
717
- }, getProgress: () => 1 };
718
- }
719
- container.style.clipPath = computeClipPath(0);
720
- } else if (prefersReduced) {
724
+ if (prefersReduced) {
725
+ if (clipDirection) container.style.clipPath = computeClipPath(1);
721
726
  onComplete?.();
722
727
  return { destroy: () => {
723
728
  }, replay: () => {
@@ -726,9 +731,12 @@ var SvgScrollDraw = (() => {
726
731
  }, seek: () => {
727
732
  }, getProgress: () => 1 };
728
733
  }
734
+ if (clipDirection) container.style.clipPath = computeClipPath(direction === "reverse" ? 1 : 0);
729
735
  const cssTiming = typeof easing === "string" ? cssTimingFor(easing) : null;
730
736
  function nativeEligible() {
731
737
  if (native === false) return false;
738
+ if (autoplay) return false;
739
+ if (debug && IS_DEV) return false;
732
740
  if (!supportsNativeTimeline()) return false;
733
741
  if (!paths.length) return false;
734
742
  if (!cssTiming) return false;
@@ -746,8 +754,8 @@ var SvgScrollDraw = (() => {
746
754
  if (delay > 0) return false;
747
755
  if (onProgress || onStart || onComplete || onEnter || onLeave || onEnterBack || onLeaveBack) return false;
748
756
  if (strokeColor != null || strokeWidth != null || fillOpacity != null) return false;
749
- if ((trigger.start ?? "top bottom").trim() !== "top bottom") return false;
750
- if ((trigger.end ?? "bottom top").trim() !== "bottom top") return false;
757
+ if ((trigger.start || "top bottom").trim() !== "top bottom") return false;
758
+ if ((trigger.end || "bottom top").trim() !== "bottom top") return false;
751
759
  return true;
752
760
  }
753
761
  function buildNative() {
@@ -842,9 +850,8 @@ var SvgScrollDraw = (() => {
842
850
  */
843
851
  refresh() {
844
852
  paths.forEach((el, i) => {
845
- lengths[i] = getElementLength(el);
853
+ measure(el, i);
846
854
  el.style.setProperty("--ssd-len", String(lengths[i]));
847
- el.style.strokeDasharray = `${lengths[i]}`;
848
855
  });
849
856
  }
850
857
  };
@@ -852,11 +859,13 @@ var SvgScrollDraw = (() => {
852
859
  if (nativeEligible()) return tracked(buildNative());
853
860
  function buildAutoplay() {
854
861
  const effectiveDuration = Math.max(1, duration);
862
+ const runMs = clipDirection ? effectiveDuration : effectiveDuration * (1 + Math.max(0, paths.length - 1) * stagger);
855
863
  let startTime = 0;
856
864
  let pausedElapsed = 0;
857
865
  let running = false;
858
866
  function applyAutoAlpha(elapsed) {
859
867
  let allDone = true;
868
+ const runRaw = Math.min(1, Math.max(0, elapsed / runMs));
860
869
  if (clipDirection) {
861
870
  const raw = Math.min(1, Math.max(0, elapsed / effectiveDuration));
862
871
  const alpha = easeFn(raw);
@@ -869,32 +878,16 @@ var SvgScrollDraw = (() => {
869
878
  paths.forEach((el, i) => {
870
879
  const staggerMs = i * stagger * effectiveDuration;
871
880
  const raw = Math.min(1, Math.max(0, (elapsed - staggerMs) / effectiveDuration));
872
- const alpha = easeFn(raw);
873
- el.style.strokeDashoffset = direction === "reverse" ? `${lengths[i] * alpha}` : `${lengths[i] * (1 - alpha)}`;
874
- if (fade) el.style.opacity = direction === "reverse" ? `${1 - alpha}` : `${alpha}`;
875
- if (colorFrom && colorTo) el.style.stroke = lerpColor(colorFrom, colorTo, alpha);
876
- else if (colorTo) el.style.stroke = colorTo;
877
- if (widthFrom !== null && widthTo !== null)
878
- el.style.strokeWidth = `${widthFrom + (widthTo - widthFrom) * alpha}`;
879
- else if (widthTo !== null)
880
- el.style.strokeWidth = `${widthTo}`;
881
- if (fillOpacityFrom !== null && fillOpacityTo !== null)
882
- el.style.fillOpacity = `${fillOpacityFrom + (fillOpacityTo - fillOpacityFrom) * alpha}`;
883
- else if (fillOpacityTo !== null)
884
- el.style.fillOpacity = `${fillOpacityTo}`;
885
- if (morphTo && el.tagName.toLowerCase() === "path" && originalDs[i])
886
- el.setAttribute("d", morphPath(originalDs[i], morphTo, alpha));
887
- if (i === 0) {
888
- currentAlpha = alpha;
889
- onProgress?.(alpha);
890
- container.style.setProperty("--scroll-draw-progress", String(alpha));
891
- }
881
+ paint(el, i, easeFn(raw), direction);
892
882
  if (raw < 1) allDone = false;
893
883
  });
884
+ const runAlpha = easeFn(runRaw);
885
+ currentAlpha = runAlpha;
886
+ onProgress?.(runAlpha);
887
+ container.style.setProperty("--scroll-draw-progress", String(runAlpha));
894
888
  }
895
889
  if (waypoints) {
896
- const globalRaw = Math.min(1, Math.max(0, elapsed / effectiveDuration));
897
- const globalAlpha = easeFn(globalRaw);
890
+ const globalAlpha = easeFn(runRaw);
898
891
  for (const key in waypoints) {
899
892
  const t = parseFloat(key);
900
893
  if (globalAlpha >= t && !firedWaypoints.has(t)) {
@@ -915,7 +908,7 @@ var SvgScrollDraw = (() => {
915
908
  const done = applyAutoAlpha(elapsed);
916
909
  if (done && !completed) {
917
910
  completed = true;
918
- applyAutoAlpha(effectiveDuration * (1 + Math.max(0, paths.length - 1) * stagger));
911
+ applyAutoAlpha(runMs);
919
912
  onComplete?.();
920
913
  const maxRepeats = repeat === "infinite" ? Infinity : repeat ?? 0;
921
914
  if (repeatCount < maxRepeats) {
@@ -965,12 +958,7 @@ var SvgScrollDraw = (() => {
965
958
  let resizeTimer2;
966
959
  function onResize2() {
967
960
  clearTimeout(resizeTimer2);
968
- resizeTimer2 = setTimeout(() => {
969
- paths.forEach((el, i) => {
970
- lengths[i] = getElementLength(el);
971
- el.style.strokeDasharray = `${lengths[i]}`;
972
- });
973
- }, 150);
961
+ resizeTimer2 = setTimeout(() => paths.forEach(measure), 150);
974
962
  }
975
963
  window.addEventListener("resize", onResize2);
976
964
  window.addEventListener("orientationchange", onResize2);
@@ -1006,7 +994,7 @@ var SvgScrollDraw = (() => {
1006
994
  const clamped = Math.min(1, Math.max(0, p));
1007
995
  currentAlpha = clamped;
1008
996
  paused = true;
1009
- pausedElapsed = clamped * effectiveDuration;
997
+ pausedElapsed = clamped * runMs;
1010
998
  startTime = performance.now() - pausedElapsed;
1011
999
  cancelAnimationFrame(rafId);
1012
1000
  applyAutoAlpha(pausedElapsed);
@@ -1017,10 +1005,7 @@ var SvgScrollDraw = (() => {
1017
1005
  /** No trigger window on this path — it runs on time — so lengths only. */
1018
1006
  refresh() {
1019
1007
  clearTimeout(resizeTimer2);
1020
- paths.forEach((el, i) => {
1021
- lengths[i] = getElementLength(el);
1022
- el.style.strokeDasharray = `${lengths[i]}`;
1023
- });
1008
+ paths.forEach(measure);
1024
1009
  }
1025
1010
  };
1026
1011
  }
@@ -1058,11 +1043,7 @@ var SvgScrollDraw = (() => {
1058
1043
  }
1059
1044
  prevRawProgress = rawProgress;
1060
1045
  if (clipDirection) {
1061
- let alpha = easeFn(computeProgress(currentScroll, tStart, tEnd, effectiveSpeed));
1062
- if (once && !autoReverse) {
1063
- frozenAlpha = Math.max(frozenAlpha, alpha);
1064
- alpha = frozenAlpha;
1065
- }
1046
+ const alpha = latch(0, computeProgress(currentScroll, tStart, tEnd, effectiveSpeed));
1066
1047
  currentAlpha = alpha;
1067
1048
  container.style.setProperty("--scroll-draw-progress", String(alpha));
1068
1049
  const visual = effectiveDir === "reverse" ? 1 - alpha : alpha;
@@ -1080,9 +1061,10 @@ var SvgScrollDraw = (() => {
1080
1061
  repeatCount++;
1081
1062
  repeatTimer = setTimeout(() => {
1082
1063
  frozenAlpha = -1;
1064
+ frozenPath = [];
1083
1065
  started = false;
1084
1066
  completed = false;
1085
- container.style.clipPath = computeClipPath(0);
1067
+ resetPaths();
1086
1068
  }, repeatDelay);
1087
1069
  }
1088
1070
  } else if (alpha < 1 && !once) {
@@ -1093,24 +1075,8 @@ var SvgScrollDraw = (() => {
1093
1075
  }
1094
1076
  paths.forEach((el, i) => {
1095
1077
  const offset = i * staggerFit * range;
1096
- let alpha = easeFn(computeProgress(currentScroll, tStart + offset, tEnd + offset, effectiveSpeed));
1097
- if (once && !autoReverse) {
1098
- alpha = frozenPath[i] = Math.max(frozenPath[i] ?? frozenAlpha, alpha);
1099
- }
1100
- el.style.strokeDashoffset = effectiveDir === "reverse" ? `${lengths[i] * alpha}` : `${lengths[i] * (1 - alpha)}`;
1101
- if (fade) el.style.opacity = effectiveDir === "reverse" ? `${1 - alpha}` : `${alpha}`;
1102
- if (colorFrom && colorTo) el.style.stroke = lerpColor(colorFrom, colorTo, alpha);
1103
- else if (colorTo) el.style.stroke = colorTo;
1104
- if (widthFrom !== null && widthTo !== null)
1105
- el.style.strokeWidth = `${widthFrom + (widthTo - widthFrom) * alpha}`;
1106
- else if (widthTo !== null)
1107
- el.style.strokeWidth = `${widthTo}`;
1108
- if (fillOpacityFrom !== null && fillOpacityTo !== null)
1109
- el.style.fillOpacity = `${fillOpacityFrom + (fillOpacityTo - fillOpacityFrom) * alpha}`;
1110
- else if (fillOpacityTo !== null)
1111
- el.style.fillOpacity = `${fillOpacityTo}`;
1112
- if (morphTo && el.tagName.toLowerCase() === "path" && originalDs[i])
1113
- el.setAttribute("d", morphPath(originalDs[i], morphTo, alpha));
1078
+ const alpha = latch(i, computeProgress(currentScroll, tStart + offset, tEnd + offset, effectiveSpeed));
1079
+ paint(el, i, alpha, effectiveDir);
1114
1080
  if (i === 0) {
1115
1081
  currentAlpha = alpha;
1116
1082
  onProgress?.(alpha);
@@ -1190,10 +1156,7 @@ var SvgScrollDraw = (() => {
1190
1156
  { root: scrollEl ?? null, threshold, rootMargin }
1191
1157
  );
1192
1158
  function remeasure() {
1193
- paths.forEach((el, i) => {
1194
- lengths[i] = getElementLength(el);
1195
- el.style.strokeDasharray = `${lengths[i]}`;
1196
- });
1159
+ paths.forEach(measure);
1197
1160
  cacheTriggers();
1198
1161
  dirty = true;
1199
1162
  }
@@ -1398,8 +1361,8 @@ var SvgScrollDraw = (() => {
1398
1361
  onLeaveBack
1399
1362
  } = options;
1400
1363
  const easeFn = typeof easing === "function" ? easing : EASINGS[easing] ?? EASINGS["ease-out"];
1401
- const startConfig = parseTrigger(trigger.start ?? "top bottom");
1402
- const endConfig = parseTrigger(trigger.end ?? "bottom top");
1364
+ const startConfig = parseTrigger(trigger.start || "top bottom");
1365
+ const endConfig = parseTrigger(trigger.end || "bottom top");
1403
1366
  const triggerEl = triggerElement ?? el;
1404
1367
  const scrollEl = typeof scrollContainer === "string" ? document.querySelector(scrollContainer) : scrollContainer ?? null;
1405
1368
  const entries = Object.entries(props).map(([key, val]) => ({
@@ -1436,6 +1399,14 @@ var SvgScrollDraw = (() => {
1436
1399
  return { ...NOOP, destroy: restoreInline, getProgress: () => 1 };
1437
1400
  }
1438
1401
  resolveFromValues();
1402
+ const scrollPos = () => {
1403
+ if (scrollEl) return axis === "x" ? scrollEl.scrollLeft : scrollEl.scrollTop;
1404
+ return axis === "x" ? window.scrollX : window.scrollY;
1405
+ };
1406
+ const vpSize = () => {
1407
+ if (scrollEl) return axis === "x" ? scrollEl.clientWidth : scrollEl.clientHeight;
1408
+ return axis === "x" ? window.innerWidth : window.innerHeight;
1409
+ };
1439
1410
  const cssTiming = typeof easing === "string" ? cssTimingFor(easing) : null;
1440
1411
  function nativeEligible() {
1441
1412
  if (!native) return false;
@@ -1448,8 +1419,8 @@ var SvgScrollDraw = (() => {
1448
1419
  if (speed !== 1) return false;
1449
1420
  if (onProgress || onComplete || onEnter || onLeave || onEnterBack || onLeaveBack) return false;
1450
1421
  if (velocityScale !== false) return false;
1451
- if ((trigger.start ?? "top bottom").trim() !== "top bottom") return false;
1452
- if ((trigger.end ?? "bottom top").trim() !== "bottom top") return false;
1422
+ if ((trigger.start || "top bottom").trim() !== "top bottom") return false;
1423
+ if ((trigger.end || "bottom top").trim() !== "bottom top") return false;
1453
1424
  for (const entry of entries) {
1454
1425
  if (!NATIVE_SAFE_PROPS.has(entry.prop)) return false;
1455
1426
  }
@@ -1464,11 +1435,9 @@ var SvgScrollDraw = (() => {
1464
1435
  style.textContent = `@keyframes ${cls}{from{${fromBody}}to{${toBody}}}.${cls}{animation-name:${cls};animation-duration:auto;animation-timing-function:${cssTiming};animation-fill-mode:both;animation-timeline:view();animation-range:cover 0% cover 100%;}`;
1465
1436
  document.head.appendChild(style);
1466
1437
  el.classList.add(cls);
1467
- const scrollPos2 = () => axis === "x" ? window.scrollX : window.scrollY;
1468
- const vpSize2 = () => axis === "x" ? window.innerWidth : window.innerHeight;
1469
1438
  const liveTriggers = () => {
1470
1439
  const rect = el.getBoundingClientRect();
1471
- return computeTriggers({ top: rect.top, height: rect.height }, scrollPos2(), vpSize2(), startConfig, endConfig);
1440
+ return computeTriggers({ top: rect.top, height: rect.height }, scrollPos(), vpSize(), startConfig, endConfig);
1472
1441
  };
1473
1442
  _register(el, {
1474
1443
  type: "animate",
@@ -1506,7 +1475,7 @@ var SvgScrollDraw = (() => {
1506
1475
  },
1507
1476
  getProgress() {
1508
1477
  const { tStart: tStart2, tEnd: tEnd2 } = liveTriggers();
1509
- return easeFn(computeProgress(scrollPos2(), tStart2, tEnd2, speed));
1478
+ return easeFn(computeProgress(scrollPos(), tStart2, tEnd2, speed));
1510
1479
  }
1511
1480
  };
1512
1481
  return instance;
@@ -1515,22 +1484,15 @@ var SvgScrollDraw = (() => {
1515
1484
  let tStart = 0;
1516
1485
  let tEnd = 0;
1517
1486
  let rafId = 0;
1518
- let isVisible = false;
1487
+ let painted = NaN;
1519
1488
  let paused = false;
1520
1489
  let frozenAlpha = -1;
1490
+ let frozenRaw = 0;
1521
1491
  let currentAlpha = 0;
1522
1492
  let completed = false;
1523
1493
  let prevRawProgress = NaN;
1524
1494
  let prevVelScroll = -1;
1525
1495
  let prevVelTime = 0;
1526
- const scrollPos = () => {
1527
- if (scrollEl) return axis === "x" ? scrollEl.scrollLeft : scrollEl.scrollTop;
1528
- return axis === "x" ? window.scrollX : window.scrollY;
1529
- };
1530
- const vpSize = () => {
1531
- if (scrollEl) return axis === "x" ? scrollEl.clientWidth : scrollEl.clientHeight;
1532
- return axis === "x" ? window.innerWidth : window.innerHeight;
1533
- };
1534
1496
  const moving = triggerEl === el ? entries : [];
1535
1497
  let measured = false;
1536
1498
  function cacheTriggers() {
@@ -1569,8 +1531,12 @@ var SvgScrollDraw = (() => {
1569
1531
  else if (prevRawProgress >= 1 && raw < 1) onEnterBack?.();
1570
1532
  prevRawProgress = raw;
1571
1533
  }
1534
+ const gate = (scroll) => computeProgress(scroll, tStart, tEnd, Math.min(speed, 1));
1535
+ function wake() {
1536
+ if (!rafId && !paused && gate(scrollPos()) !== painted) rafId = requestAnimationFrame(update);
1537
+ }
1572
1538
  function update() {
1573
- if (!isVisible || paused) return;
1539
+ if (paused) return;
1574
1540
  const now = performance.now();
1575
1541
  const currentScroll = scrollPos();
1576
1542
  let effectiveSpeed = speed;
@@ -1584,11 +1550,8 @@ var SvgScrollDraw = (() => {
1584
1550
  prevVelTime = now;
1585
1551
  const raw = tEnd === tStart ? 0 : (currentScroll - tStart) / (tEnd - tStart);
1586
1552
  fireScrollCallbacks(raw);
1587
- let alpha = easeFn(computeProgress(currentScroll, tStart, tEnd, effectiveSpeed));
1588
- if (once) {
1589
- frozenAlpha = Math.max(frozenAlpha, alpha);
1590
- alpha = frozenAlpha;
1591
- }
1553
+ const progress = computeProgress(currentScroll, tStart, tEnd, effectiveSpeed);
1554
+ const alpha = once ? Math.max(frozenAlpha, easeFn(frozenRaw = Math.max(frozenRaw, progress))) : easeFn(progress);
1592
1555
  currentAlpha = alpha;
1593
1556
  applyAlpha(alpha);
1594
1557
  if (alpha >= 1 && !completed) {
@@ -1597,49 +1560,49 @@ var SvgScrollDraw = (() => {
1597
1560
  } else if (alpha < 1 && !once) {
1598
1561
  completed = false;
1599
1562
  }
1600
- rafId = requestAnimationFrame(update);
1563
+ if (paused) return;
1564
+ painted = gate(currentScroll);
1565
+ rafId = painted > 0 && painted < 1 ? requestAnimationFrame(update) : 0;
1601
1566
  }
1602
1567
  cacheTriggers();
1603
1568
  if (tEnd === tStart) {
1604
- warn(
1605
- "scrollAnimate: the trigger window has zero length (start and end resolve to the same scroll position), so this element will never animate. If the target is inside a position:sticky stage, measure the trigger from the container that holds the scroll room via `triggerElement`.",
1606
- el
1607
- );
1569
+ warn("scrollAnimate: zero-length trigger window, so it never animates. In a sticky stage, set triggerElement.", el);
1608
1570
  }
1609
1571
  {
1610
- const initAlpha = easeFn(computeProgress(scrollPos(), tStart, tEnd, speed));
1611
- if (once && initAlpha > 0) frozenAlpha = initAlpha;
1612
- currentAlpha = initAlpha;
1613
- applyAlpha(initAlpha);
1572
+ frozenRaw = computeProgress(scrollPos(), tStart, tEnd, speed);
1573
+ currentAlpha = easeFn(frozenRaw);
1574
+ applyAlpha(currentAlpha);
1575
+ }
1576
+ function remeasure() {
1577
+ cacheTriggers();
1578
+ wake();
1614
1579
  }
1615
- const observer2 = new IntersectionObserver(
1616
- (ioEntries) => {
1617
- ioEntries.forEach((e) => {
1618
- isVisible = e.isIntersecting;
1619
- if (isVisible && !paused) rafId = requestAnimationFrame(update);
1620
- else cancelAnimationFrame(rafId);
1621
- });
1622
- },
1623
- { root: scrollEl ?? null }
1624
- );
1625
1580
  let resizeTimer;
1626
1581
  function onResize() {
1627
1582
  clearTimeout(resizeTimer);
1628
- resizeTimer = setTimeout(cacheTriggers, 150);
1583
+ resizeTimer = setTimeout(remeasure, 150);
1629
1584
  }
1585
+ const scroller = scrollEl ?? window;
1586
+ scroller.addEventListener("scroll", wake, { passive: true });
1630
1587
  window.addEventListener("resize", onResize);
1631
1588
  window.addEventListener("orientationchange", onResize);
1632
1589
  const unwatch = watchLayout([triggerEl], onResize);
1633
- observer2.observe(el);
1590
+ wake();
1634
1591
  _register(el, {
1635
1592
  type: "animate",
1636
1593
  getProgress: () => currentAlpha,
1637
1594
  getTrigger: () => ({ tStart, tEnd })
1638
1595
  });
1596
+ function halt() {
1597
+ paused = true;
1598
+ cancelAnimationFrame(rafId);
1599
+ rafId = 0;
1600
+ painted = NaN;
1601
+ }
1639
1602
  return {
1640
1603
  destroy() {
1641
- cancelAnimationFrame(rafId);
1642
- observer2.disconnect();
1604
+ halt();
1605
+ scroller.removeEventListener("scroll", wake);
1643
1606
  window.removeEventListener("resize", onResize);
1644
1607
  window.removeEventListener("orientationchange", onResize);
1645
1608
  unwatch();
@@ -1649,34 +1612,32 @@ var SvgScrollDraw = (() => {
1649
1612
  },
1650
1613
  replay() {
1651
1614
  frozenAlpha = -1;
1615
+ frozenRaw = 0;
1652
1616
  completed = false;
1653
1617
  currentAlpha = 0;
1654
1618
  paused = false;
1655
1619
  applyAlpha(0);
1620
+ painted = 0;
1621
+ wake();
1656
1622
  },
1657
- pause() {
1658
- paused = true;
1659
- cancelAnimationFrame(rafId);
1660
- },
1623
+ pause: halt,
1661
1624
  resume() {
1662
1625
  if (!paused) return;
1663
1626
  paused = false;
1664
- if (isVisible) rafId = requestAnimationFrame(update);
1627
+ wake();
1665
1628
  },
1666
1629
  seek(p) {
1667
1630
  const clamped = Math.min(1, Math.max(0, p));
1668
1631
  currentAlpha = clamped;
1669
1632
  frozenAlpha = clamped;
1670
- paused = true;
1671
- cancelAnimationFrame(rafId);
1633
+ frozenRaw = 0;
1634
+ halt();
1672
1635
  applyAlpha(clamped);
1673
1636
  },
1674
1637
  getProgress() {
1675
1638
  return currentAlpha;
1676
1639
  },
1677
- refresh() {
1678
- cacheTriggers();
1679
- }
1640
+ refresh: remeasure
1680
1641
  };
1681
1642
  }
1682
1643
 
@@ -1766,8 +1727,8 @@ var SvgScrollDraw = (() => {
1766
1727
  } = options;
1767
1728
  const fmt = decimals !== void 0 ? (n) => n.toFixed(decimals) : format ?? ((n) => String(Math.round(n)));
1768
1729
  const easeFn = typeof easing === "function" ? easing : EASINGS[easing] ?? EASINGS["ease-out"];
1769
- const startConfig = parseTrigger(trigger.start ?? "top 80%");
1770
- const endConfig = parseTrigger(trigger.end ?? "top 20%");
1730
+ const startConfig = parseTrigger(trigger.start || "top 80%");
1731
+ const endConfig = parseTrigger(trigger.end || "top 20%");
1771
1732
  if (prefersReduced) {
1772
1733
  el.textContent = fmt(to);
1773
1734
  onComplete?.();
@@ -1780,6 +1741,7 @@ var SvgScrollDraw = (() => {
1780
1741
  let isVisible = false;
1781
1742
  let paused = false;
1782
1743
  let frozenAlpha = -1;
1744
+ let frozenRaw = 0;
1783
1745
  let currentAlpha = 0;
1784
1746
  let completed = false;
1785
1747
  const scrollPos = () => window.scrollY;
@@ -1798,17 +1760,15 @@ var SvgScrollDraw = (() => {
1798
1760
  tStart = result.tStart;
1799
1761
  tEnd = result.tEnd;
1800
1762
  }
1763
+ const savedVar = el.style.getPropertyValue("--scroll-draw-progress");
1801
1764
  function applyAlpha(alpha) {
1802
1765
  el.textContent = fmt(from + (to - from) * alpha);
1803
1766
  el.style.setProperty("--scroll-draw-progress", String(alpha));
1804
1767
  }
1805
1768
  function update() {
1806
1769
  if (!isVisible || paused) return;
1807
- let alpha = easeFn(computeProgress(scrollPos(), tStart, tEnd, 1));
1808
- if (once) {
1809
- frozenAlpha = Math.max(frozenAlpha, alpha);
1810
- alpha = frozenAlpha;
1811
- }
1770
+ const progress = computeProgress(scrollPos(), tStart, tEnd, 1);
1771
+ const alpha = once ? Math.max(frozenAlpha, easeFn(frozenRaw = Math.max(frozenRaw, progress))) : easeFn(progress);
1812
1772
  currentAlpha = alpha;
1813
1773
  applyAlpha(alpha);
1814
1774
  if (alpha >= 1 && !completed) {
@@ -1821,8 +1781,8 @@ var SvgScrollDraw = (() => {
1821
1781
  }
1822
1782
  cacheTriggers();
1823
1783
  {
1824
- const initAlpha = easeFn(computeProgress(scrollPos(), tStart, tEnd, 1));
1825
- if (once && initAlpha > 0) frozenAlpha = initAlpha;
1784
+ frozenRaw = computeProgress(scrollPos(), tStart, tEnd, 1);
1785
+ const initAlpha = easeFn(frozenRaw);
1826
1786
  currentAlpha = initAlpha;
1827
1787
  applyAlpha(initAlpha);
1828
1788
  }
@@ -1849,6 +1809,11 @@ var SvgScrollDraw = (() => {
1849
1809
  getProgress: () => currentAlpha,
1850
1810
  getTrigger: () => ({ tStart, tEnd })
1851
1811
  });
1812
+ function resume() {
1813
+ if (!paused) return;
1814
+ paused = false;
1815
+ if (isVisible) rafId = requestAnimationFrame(update);
1816
+ }
1852
1817
  return {
1853
1818
  destroy() {
1854
1819
  cancelAnimationFrame(rafId);
@@ -1857,28 +1822,27 @@ var SvgScrollDraw = (() => {
1857
1822
  window.removeEventListener("orientationchange", onResize);
1858
1823
  unwatch();
1859
1824
  clearTimeout(resizeTimer);
1825
+ el.style.setProperty("--scroll-draw-progress", savedVar);
1860
1826
  _unregister(el);
1861
1827
  },
1862
1828
  replay() {
1863
1829
  frozenAlpha = -1;
1830
+ frozenRaw = 0;
1864
1831
  completed = false;
1865
1832
  currentAlpha = 0;
1866
- paused = false;
1867
1833
  applyAlpha(0);
1834
+ resume();
1868
1835
  },
1869
1836
  pause() {
1870
1837
  paused = true;
1871
1838
  cancelAnimationFrame(rafId);
1872
1839
  },
1873
- resume() {
1874
- if (!paused) return;
1875
- paused = false;
1876
- if (isVisible) rafId = requestAnimationFrame(update);
1877
- },
1840
+ resume,
1878
1841
  seek(p) {
1879
1842
  const clamped = Math.min(1, Math.max(0, p));
1880
1843
  currentAlpha = clamped;
1881
1844
  frozenAlpha = clamped;
1845
+ frozenRaw = 0;
1882
1846
  paused = true;
1883
1847
  cancelAnimationFrame(rafId);
1884
1848
  applyAlpha(clamped);