svg-scroll-draw 2.13.0 → 2.15.0

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.
@@ -1,3 +1,5 @@
1
+ import { RefObject } from 'react';
2
+
1
3
  type VivusType = 'delayed' | 'sync' | 'async' | 'nsync' | 'oneByOne' | 'scenario' | 'scenario-sync';
2
4
  type VivusStart = 'inViewport' | 'manual' | 'autostart';
3
5
  type VivusTiming = (x: number) => number;
@@ -97,4 +99,18 @@ declare class Vivus {
97
99
  isInViewport(el: Element, h?: number): boolean;
98
100
  }
99
101
 
100
- export { Vivus, type VivusCallback, type VivusOptions, type VivusStart, type VivusTiming, type VivusType, Vivus as default };
102
+ /**
103
+ * svg-scroll-draw/vivus/react — the vivus drop-in for React.
104
+ *
105
+ * `new Vivus()` needs the mounted <svg>, and the instance needs destroying when
106
+ * the component goes, which vivus users wire up by hand in every component. This
107
+ * hook does both and hands the instance back for `play()`, `reset()` and the
108
+ * rest. StrictMode's double mount is safe: the first instance is destroyed
109
+ * before the second is made.
110
+ *
111
+ * Options and the callback are read once, on mount.
112
+ */
113
+
114
+ declare function useVivus<T extends Element = SVGSVGElement>(options?: VivusOptions, callback?: VivusCallback): [RefObject<T>, Vivus | null];
115
+
116
+ export { useVivus };
@@ -1,3 +1,5 @@
1
+ import { RefObject } from 'react';
2
+
1
3
  type VivusType = 'delayed' | 'sync' | 'async' | 'nsync' | 'oneByOne' | 'scenario' | 'scenario-sync';
2
4
  type VivusStart = 'inViewport' | 'manual' | 'autostart';
3
5
  type VivusTiming = (x: number) => number;
@@ -97,4 +99,18 @@ declare class Vivus {
97
99
  isInViewport(el: Element, h?: number): boolean;
98
100
  }
99
101
 
100
- export { Vivus, type VivusCallback, type VivusOptions, type VivusStart, type VivusTiming, type VivusType, Vivus as default };
102
+ /**
103
+ * svg-scroll-draw/vivus/react — the vivus drop-in for React.
104
+ *
105
+ * `new Vivus()` needs the mounted <svg>, and the instance needs destroying when
106
+ * the component goes, which vivus users wire up by hand in every component. This
107
+ * hook does both and hands the instance back for `play()`, `reset()` and the
108
+ * rest. StrictMode's double mount is safe: the first instance is destroyed
109
+ * before the second is made.
110
+ *
111
+ * Options and the callback are read once, on mount.
112
+ */
113
+
114
+ declare function useVivus<T extends Element = SVGSVGElement>(options?: VivusOptions, callback?: VivusCallback): [RefObject<T>, Vivus | null];
115
+
116
+ export { useVivus };
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{useEffect as T,useRef as y,useState as k}from"react";function g(){return typeof window>"u"||typeof window.matchMedia!="function"?!1:window.matchMedia("(prefers-reduced-motion: reduce)").matches}var V=typeof __SVG_SCROLL_DRAW_DEV__<"u"?__SVG_SCROLL_DRAW_DEV__:typeof process<"u"&&typeof process.env<"u"&&process.env.NODE_ENV!=="production";function m(r,...t){V&&console.warn(`[svg-scroll-draw] ${r}`,...t)}var L=["delayed","sync","async","nsync","oneByOne","scenario","scenario-sync"],R=["inViewport","manual","autostart"];function u(r,t){let e=parseInt(r,10);return e>=0?e:t}function S(r){if(typeof window>"u")return!1;let t=window,e=n=>typeof t[n]=="function"&&r instanceof t[n];return e("SVGSVGElement")||e("SVGGElement")||r?.constructor===t.SVGElement}var v=["cx","cy","points","r","rx","ry","x","x1","x2","y","y1","y2"],x=["line","ellipse","circle","polygon","polyline","rect"],$="http://www.w3.org/2000/svg";function b(r){let t=r.points.trim().split(" ");if(r.points.indexOf(",")===-1){let n=[];for(let s=0;s<t.length;s+=2)n.push(`${t[s]},${t[s+1]}`);t=n}let e=`M${t[0]}`;for(let n=1;n<t.length;n++)t[n].indexOf(",")!==-1&&(e+=`L${t[n]}`);return e}function w(r,t,e,n){let s=parseFloat(e)||0,a=parseFloat(n)||0,i=parseFloat(r)||0,o=parseFloat(t)||0,l=i-s,h=i+s;return`M${l},${o}A${s},${a} 0,1,1 ${h},${o}A${s},${a} 0,1,1 ${l},${o}`}var A={line:r=>`M${r.x1||0},${r.y1||0}L${r.x2||0},${r.y2||0}`,rect(r){let t=parseFloat(r.x)||0,e=parseFloat(r.y)||0,n=parseFloat(r.width)||0,s=parseFloat(r.height)||0;if(r.rx||r.ry){let a=parseInt(r.rx,10)||-1,i=parseInt(r.ry,10)||-1;a=Math.min(Math.max(a<0?i:a,0),n/2),i=Math.min(Math.max(i<0?a:i,0),s/2);let o=`A ${a},${i},0,0,1,`;return`M ${t+a},${e} L ${t+n-a},${e} ${o}${t+n},${e+i} L ${t+n},${e+s-i} ${o}${t+n-a},${e+s} L ${t+a},${e+s} ${o}${t},${e+s-i} L ${t},${e+i} ${o}${t+a},${e}`}return`M${t} ${e} L${t+n} ${e} L${t+n} ${e+s} L${t} ${e+s} Z`},polyline:b,polygon:r=>`${b(r)}Z`,ellipse:r=>w(r.cx,r.cy,r.rx,r.ry),circle:r=>w(r.cx,r.cy,r.r,r.r)};function M(r){for(let t of Array.from(r.querySelectorAll(x.join(",")))){let e={};for(let{name:a,value:i}of Array.from(t.attributes)){if(v.indexOf(a)!==-1&&i.indexOf("%")!==-1)throw new Error("Pathformer [parseAttr]: a SVG shape got values in percentage. This cannot be transformed into 'path' tags. Please use 'viewBox'.");e[a]=i}let n=A[t.tagName.toLowerCase()](e),s=document.createElementNS($,"path");for(let{name:a,value:i}of Array.from(t.attributes))v.indexOf(a)===-1&&s.setAttribute(a,i);s.setAttribute("d",n),t.parentNode?.replaceChild(s,t)}}var c=class c{constructor(t,e,n){this.isReady=!1;this.type="delayed";this.start="inViewport";this.duration=120;this.delay=null;this.dashGap=1;this.forceRender=!1;this.reverseStack=!1;this.selfDestroy=!1;this.ignoreInvisible=!1;this.animTimingFunction=c.LINEAR;this.pathTimingFunction=c.LINEAR;this.callback=()=>{};this.map=[];this.frameLength=0;this.currentFrame=0;this.delayUnit=0;this.speed=1;this.handle=null;this.instanceCallback=null;this.mustRecalcScale=null;this.viewportListener=null;this.setElement(t,e),this.setOptions(e),this.setCallback(n),this.isReady&&this.init()}setElement(t,e){if(t===void 0)throw new Error('Vivus [constructor]: "element" parameter is required');let n=t;if(typeof n=="string"){let s=document.getElementById(n);if(!s)throw new Error('Vivus [constructor]: "element" parameter is not related to an existing ID');n=s}if(this.parentEl=n,e&&e.file){let s=new XMLHttpRequest;s.addEventListener("load",()=>{let a=document.createElement("div");a.innerHTML=s.responseText;let i=a.querySelector("svg");if(!i)throw new Error(`Vivus [load]: Cannot find the SVG in the loaded file : ${e.file}`);this.el=i,i.setAttribute("width","100%"),i.setAttribute("height","100%"),this.parentEl.appendChild(i),this.isReady=!0,this.init()}),s.open("GET",e.file),s.send();return}if(S(n)){this.el=n,this.isReady=!0;return}if(n instanceof HTMLObjectElement){let s=n,a=i=>{if(this.isReady)return;let o=s.contentDocument&&s.contentDocument.querySelector("svg");if(!o&&i)throw new Error("Vivus [constructor]: object loaded does not contain any SVG");o&&(s.getAttribute("built-by-vivus")&&(this.parentEl.insertBefore(o,s),this.parentEl.removeChild(s),o.setAttribute("width","100%"),o.setAttribute("height","100%")),this.el=o,this.isReady=!0,this.init())};a(),this.isReady||s.addEventListener("load",a);return}throw new Error('Vivus [constructor]: "element" parameter is not valid (or miss the "file" attribute)')}setOptions(t){if(t!==void 0&&(t===null||t.constructor!==Object))throw new Error('Vivus [constructor]: "options" parameter must be an object');let e=t||{};if(e.type&&L.indexOf(e.type)===-1)throw new Error(`Vivus [constructor]: ${e.type} is not an existing animation \`type\``);if(e.start&&R.indexOf(e.start)===-1)throw new Error(`Vivus [constructor]: ${e.start} is not an existing \`start\` option`);this.type=e.type||"delayed",this.start=e.start||"inViewport";let n=navigator.userAgent,s=n.indexOf("MSIE")!==-1||n.indexOf("Trident/")!==-1||n.indexOf("Edge/")!==-1;if(this.duration=u(e.duration,120),this.delay=u(e.delay,null),this.dashGap=u(e.dashGap,1),this.forceRender=Object.prototype.hasOwnProperty.call(e,"forceRender")?!!e.forceRender:s,this.reverseStack=!!e.reverseStack,this.selfDestroy=!!e.selfDestroy,this.onReady=e.onReady,this.map=[],this.ignoreInvisible=Object.prototype.hasOwnProperty.call(e,"ignoreInvisible")?!!e.ignoreInvisible:!1,this.animTimingFunction=e.animTimingFunction||c.LINEAR,this.pathTimingFunction=e.pathTimingFunction||c.LINEAR,this.delay!==null&&this.delay>=this.duration)throw new Error("Vivus [constructor]: delay must be shorter than duration")}setCallback(t){if(t&&typeof t!="function")throw new Error('Vivus [constructor]: "callback" parameter must be a function');this.callback=t||(()=>{})}mapping(){let t=0,e=0,n=0,s=!1,a=Array.from(this.el.querySelectorAll("path"));a.forEach((i,o)=>{if(this.isInvisible(i))return;let l={el:i,length:0,startAt:0,duration:0,isResizeSensitive:!1},h=1;if(i.getAttribute("vector-effect")==="non-scaling-stroke"){let d=i.getBoundingClientRect(),f=i.getBBox();h=Math.max(d.width/f.width,d.height/f.height),l.isResizeSensitive=!0,s=!0}if(l.length=Math.ceil(i.getTotalLength()*h),isNaN(l.length)){m("vivus: cannot retrieve a path element length",i);return}this.map.push(l),i.style.strokeDasharray=`${l.length} ${l.length+this.dashGap*2}`,i.style.strokeDashoffset=String(l.length+this.dashGap),l.length+=this.dashGap,e+=l.length,this.renderPath(o)}),s&&m("vivus: this SVG contains non-scaling-strokes. Call instance.recalc() when the SVG is resized."),e=e===0?1:e,this.delay=this.delay===null?this.duration/3:this.delay,this.delayUnit=this.delay/(a.length>1?a.length-1:1),this.reverseStack&&this.map.reverse(),this.map.forEach((i,o)=>{let l=()=>{let h={};for(let{name:d,value:f}of Array.from(i.el.attributes))h[d]=f;return h};switch(this.type){case"delayed":i.startAt=this.delayUnit*o,i.duration=this.duration-this.delay;break;case"oneByOne":i.startAt=n/e*this.duration,i.duration=i.length/e*this.duration;break;case"sync":case"async":case"nsync":i.startAt=0,i.duration=this.duration;break;case"scenario-sync":{let h=l();i.startAt=t+(u(h["data-delay"],this.delayUnit)||0),i.duration=u(h["data-duration"],this.duration),t=h["data-async"]!==void 0?i.startAt:i.startAt+i.duration,this.frameLength=Math.max(this.frameLength,i.startAt+i.duration);break}case"scenario":{let h=l();i.startAt=u(h["data-start"],this.delayUnit)||0,i.duration=u(h["data-duration"],this.duration),this.frameLength=Math.max(this.frameLength,i.startAt+i.duration);break}}n+=i.length,this.frameLength=this.frameLength||this.duration})}recalc(){this.mustRecalcScale||(this.mustRecalcScale=requestAnimationFrame(()=>this.performLineRecalc()))}performLineRecalc(){for(let t of this.map){if(!t.isResizeSensitive)continue;let e=t.el.getBoundingClientRect(),n=t.el.getBBox(),s=Math.max(e.width/n.width,e.height/n.height);t.length=Math.ceil(t.el.getTotalLength()*s),t.el.style.strokeDasharray=`${t.length} ${t.length+this.dashGap*2}`}this.trace(),this.mustRecalcScale=null}draw(){if(this.currentFrame+=this.speed,this.currentFrame<=0)this.stop(),this.reset();else if(this.currentFrame>=this.frameLength)this.stop(),this.currentFrame=this.frameLength,this.trace(),this.selfDestroy&&this.destroy();else{this.trace(),this.handle=requestAnimationFrame(()=>this.draw());return}this.finished()}finished(){if(this.callback(this),this.instanceCallback){let t=this.instanceCallback;this.instanceCallback=null,t(this)}}trace(){let t=this.animTimingFunction(this.currentFrame/this.frameLength)*this.frameLength;this.map.forEach((e,n)=>{let s=this.pathTimingFunction(Math.max(0,Math.min(1,(t-e.startAt)/e.duration)));e.progress!==s&&(e.progress=s,e.el.style.strokeDashoffset=String(Math.floor(e.length*(1-s))),this.renderPath(n))})}renderPath(t){let e=this.forceRender&&this.map&&this.map[t];if(!e)return;let n=e.el.cloneNode(!0);e.el.parentNode?.replaceChild(n,e.el),e.el=n}init(){this.frameLength=0,this.currentFrame=0,this.map=[],M(this.el),this.mapping(),this.starter(),this.onReady&&this.onReady(this)}starter(){if(this.start==="manual")return;if(this.start==="autostart"){this.play();return}let t=()=>{this.isInViewport(this.parentEl,1)&&(this.play(),window.removeEventListener("scroll",t),this.viewportListener=null)};this.viewportListener=t,window.addEventListener("scroll",t),t()}getStatus(){return this.currentFrame===0?"start":this.currentFrame===this.frameLength?"end":"progress"}reset(){return this.setFrameProgress(0)}finish(){return this.setFrameProgress(1)}setFrameProgress(t){let e=Math.min(1,Math.max(0,t));return this.currentFrame=Math.round(this.frameLength*e),this.trace(),this}play(t,e){this.instanceCallback=null;let n=t;if(n&&typeof n=="function")this.instanceCallback=n,n=void 0;else if(n&&typeof n!="number")throw new Error("Vivus [play]: invalid speed");return e&&typeof e=="function"&&!this.instanceCallback&&(this.instanceCallback=e),this.speed=n||1,g()?(this.stop(),this.currentFrame=this.speed<0?0:this.frameLength,this.trace(),this.speed>0&&this.selfDestroy&&this.destroy(),this.finished(),this):(this.handle||this.draw(),this)}stop(){return this.handle&&(cancelAnimationFrame(this.handle),this.handle=null),this}destroy(){this.stop(),this.viewportListener&&(window.removeEventListener("scroll",this.viewportListener),this.viewportListener=null),this.map.forEach((t,e)=>{t.el.style.strokeDashoffset="",t.el.style.strokeDasharray="",this.renderPath(e)})}isInvisible(t){let e=t.getAttribute("data-ignore");if(e!==null)return e!=="false";if(this.ignoreInvisible){let n=t.getBoundingClientRect();return!n.width&&!n.height}return!1}isInViewport(t,e=0){let n=window.pageYOffset||document.documentElement.scrollTop,s=n+Math.max(document.documentElement.clientHeight,window.innerHeight),a=t.getBoundingClientRect(),i=n+a.top;return i+a.height*(e||0)<=s&&i+a.height>=n}};c.LINEAR=t=>t,c.EASE=t=>-Math.cos(t*Math.PI)/2+.5,c.EASE_OUT=t=>1-Math.pow(1-t,3),c.EASE_IN=t=>Math.pow(t,3),c.EASE_OUT_BOUNCE=t=>{let e=-Math.cos(t*(.5*Math.PI))+1,n=Math.pow(e,1.5),s=Math.pow(1-t,2),a=-Math.abs(Math.cos(n*(2.5*Math.PI)))+1;return 1-s+a*s};var p=c,E=p;function _(r={},t){let e=y(null),[n,s]=k(null),a=y(r),i=y(t);return a.current=r,i.current=t,T(()=>{if(!e.current)return;let o=new E(e.current,a.current,l=>i.current?.(l));return s(o),()=>{o.destroy(),s(null)}},[]),[e,n]}export{_ as useVivus};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svg-scroll-draw",
3
- "version": "2.13.0",
3
+ "version": "2.15.0",
4
4
  "description": "Scroll-driven animation for the web — draw SVG paths, reveal elements, pin sections, scrub video. Zero dependencies, native CSS fast path, works with React, Vue, Svelte, Solid, Angular, Astro and vanilla JS",
5
5
  "keywords": [
6
6
  "svg",
@@ -66,7 +66,12 @@
66
66
  "vivus",
67
67
  "vivus-alternative",
68
68
  "svg-line-drawing",
69
- "line-drawing"
69
+ "line-drawing",
70
+ "react-aos",
71
+ "aos-react",
72
+ "scrollreveal-react",
73
+ "vivus-react",
74
+ "nextjs-aos"
70
75
  ],
71
76
  "homepage": "https://svg-scroll-draw.vercel.app",
72
77
  "repository": {
@@ -158,6 +163,15 @@
158
163
  ],
159
164
  "vivus": [
160
165
  "./dist/vivus/index.d.ts"
166
+ ],
167
+ "aos/react": [
168
+ "./dist/aos/react/index.d.ts"
169
+ ],
170
+ "scrollreveal/react": [
171
+ "./dist/scrollreveal/react/index.d.ts"
172
+ ],
173
+ "vivus/react": [
174
+ "./dist/vivus/react/index.d.ts"
161
175
  ]
162
176
  }
163
177
  },
@@ -407,6 +421,36 @@
407
421
  "default": "./dist/vivus/index.cjs"
408
422
  }
409
423
  },
424
+ "./aos/react": {
425
+ "import": {
426
+ "types": "./dist/aos/react/index.d.mts",
427
+ "default": "./dist/aos/react/index.mjs"
428
+ },
429
+ "require": {
430
+ "types": "./dist/aos/react/index.d.ts",
431
+ "default": "./dist/aos/react/index.cjs"
432
+ }
433
+ },
434
+ "./scrollreveal/react": {
435
+ "import": {
436
+ "types": "./dist/scrollreveal/react/index.d.mts",
437
+ "default": "./dist/scrollreveal/react/index.mjs"
438
+ },
439
+ "require": {
440
+ "types": "./dist/scrollreveal/react/index.d.ts",
441
+ "default": "./dist/scrollreveal/react/index.cjs"
442
+ }
443
+ },
444
+ "./vivus/react": {
445
+ "import": {
446
+ "types": "./dist/vivus/react/index.d.mts",
447
+ "default": "./dist/vivus/react/index.mjs"
448
+ },
449
+ "require": {
450
+ "types": "./dist/vivus/react/index.d.ts",
451
+ "default": "./dist/vivus/react/index.cjs"
452
+ }
453
+ },
410
454
  "./package.json": "./package.json"
411
455
  },
412
456
  "scripts": {
@@ -1,51 +0,0 @@
1
- /**
2
- * Lenis smooth-scroll adapter for svg-scroll-draw.
3
- *
4
- * Lenis v2+ patches `window.scrollY` natively — no adapter needed.
5
- * This adapter targets Lenis v1, which uses a virtual scroll value
6
- * and does NOT update `window.scrollY`. It patches `window.scrollY` /
7
- * `window.pageYOffset` so all svg-scroll-draw engines read the correct value.
8
- *
9
- * Usage:
10
- * import Lenis from '@studio-freight/lenis'
11
- * import { createLenisAdapter } from 'svg-scroll-draw/lenis'
12
- *
13
- * const lenis = new Lenis()
14
- * const adapter = createLenisAdapter(lenis)
15
- *
16
- * // RAF loop — let Lenis drive the tick
17
- * function raf(time: number) {
18
- * lenis.raf(time)
19
- * requestAnimationFrame(raf)
20
- * }
21
- * requestAnimationFrame(raf)
22
- *
23
- * // On cleanup:
24
- * adapter.destroy()
25
- */
26
- interface LenisScrollEvent {
27
- scroll: number;
28
- velocity: number;
29
- direction: number;
30
- progress: number;
31
- }
32
- interface LenisLike {
33
- on(event: 'scroll', callback: (e: LenisScrollEvent) => void): void;
34
- off(event: 'scroll', callback: (e: LenisScrollEvent) => void): void;
35
- }
36
- interface LenisAdapterInstance {
37
- /** Restore native `window.scrollY` and remove the Lenis listener. */
38
- destroy: () => void;
39
- /** Returns the current virtual scroll Y value Lenis last reported. */
40
- getScrollY: () => number;
41
- }
42
- /**
43
- * Patches `window.scrollY` and `window.pageYOffset` to return Lenis's
44
- * virtual scroll position so all svg-scroll-draw engines stay in sync
45
- * with smooth scroll.
46
- *
47
- * Only needed for Lenis v1. Lenis v2+ patches scrollY itself.
48
- */
49
- declare function createLenisAdapter(lenis: LenisLike): LenisAdapterInstance;
50
-
51
- export { type LenisAdapterInstance, type LenisLike, type LenisScrollEvent, createLenisAdapter };
@@ -1,51 +0,0 @@
1
- /**
2
- * Lenis smooth-scroll adapter for svg-scroll-draw.
3
- *
4
- * Lenis v2+ patches `window.scrollY` natively — no adapter needed.
5
- * This adapter targets Lenis v1, which uses a virtual scroll value
6
- * and does NOT update `window.scrollY`. It patches `window.scrollY` /
7
- * `window.pageYOffset` so all svg-scroll-draw engines read the correct value.
8
- *
9
- * Usage:
10
- * import Lenis from '@studio-freight/lenis'
11
- * import { createLenisAdapter } from 'svg-scroll-draw/lenis'
12
- *
13
- * const lenis = new Lenis()
14
- * const adapter = createLenisAdapter(lenis)
15
- *
16
- * // RAF loop — let Lenis drive the tick
17
- * function raf(time: number) {
18
- * lenis.raf(time)
19
- * requestAnimationFrame(raf)
20
- * }
21
- * requestAnimationFrame(raf)
22
- *
23
- * // On cleanup:
24
- * adapter.destroy()
25
- */
26
- interface LenisScrollEvent {
27
- scroll: number;
28
- velocity: number;
29
- direction: number;
30
- progress: number;
31
- }
32
- interface LenisLike {
33
- on(event: 'scroll', callback: (e: LenisScrollEvent) => void): void;
34
- off(event: 'scroll', callback: (e: LenisScrollEvent) => void): void;
35
- }
36
- interface LenisAdapterInstance {
37
- /** Restore native `window.scrollY` and remove the Lenis listener. */
38
- destroy: () => void;
39
- /** Returns the current virtual scroll Y value Lenis last reported. */
40
- getScrollY: () => number;
41
- }
42
- /**
43
- * Patches `window.scrollY` and `window.pageYOffset` to return Lenis's
44
- * virtual scroll position so all svg-scroll-draw engines stay in sync
45
- * with smooth scroll.
46
- *
47
- * Only needed for Lenis v1. Lenis v2+ patches scrollY itself.
48
- */
49
- declare function createLenisAdapter(lenis: LenisLike): LenisAdapterInstance;
50
-
51
- export { type LenisAdapterInstance, type LenisLike, type LenisScrollEvent, createLenisAdapter };