lenis 1.1.13 → 1.1.14-dev.3

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/lenis.mjs CHANGED
@@ -1,2 +1,854 @@
1
- function clamp(t,i,e){return Math.max(t,Math.min(i,e))}class Animate{constructor(){this.isRunning=!1,this.value=0,this.from=0,this.to=0,this.currentTime=0}advance(t){var i;if(!this.isRunning)return;let e=!1;if(this.duration&&this.easing){this.currentTime+=t;const i=clamp(0,this.currentTime/this.duration,1);e=i>=1;const s=e?1:this.easing(i);this.value=this.from+(this.to-this.from)*s}else this.lerp?(this.value=function damp(t,i,e,s){return function lerp(t,i,e){return(1-e)*t+e*i}(t,i,1-Math.exp(-e*s))}(this.value,this.to,60*this.lerp,t),Math.round(this.value)===this.to&&(this.value=this.to,e=!0)):(this.value=this.to,e=!0);e&&this.stop(),null===(i=this.onUpdate)||void 0===i||i.call(this,this.value,e)}stop(){this.isRunning=!1}fromTo(t,i,{lerp:e,duration:s,easing:o,onStart:n,onUpdate:l}){this.from=this.value=t,this.to=i,this.lerp=e,this.duration=s,this.easing=o,this.currentTime=0,this.isRunning=!0,null==n||n(),this.onUpdate=l}}class Dimensions{constructor(t,i,{autoResize:e=!0,debounce:s=250}={}){this.wrapper=t,this.content=i,this.width=0,this.height=0,this.scrollHeight=0,this.scrollWidth=0,this.resize=()=>{this.onWrapperResize(),this.onContentResize()},this.onWrapperResize=()=>{this.wrapper instanceof Window?(this.width=window.innerWidth,this.height=window.innerHeight):(this.width=this.wrapper.clientWidth,this.height=this.wrapper.clientHeight)},this.onContentResize=()=>{this.wrapper instanceof Window?(this.scrollHeight=this.content.scrollHeight,this.scrollWidth=this.content.scrollWidth):(this.scrollHeight=this.wrapper.scrollHeight,this.scrollWidth=this.wrapper.scrollWidth)},e&&(this.debouncedResize=function debounce(t,i){let e;return function(...s){let o=this;clearTimeout(e),e=setTimeout((()=>{e=void 0,t.apply(o,s)}),i)}}(this.resize,s),this.wrapper instanceof Window?window.addEventListener("resize",this.debouncedResize,!1):(this.wrapperResizeObserver=new ResizeObserver(this.debouncedResize),this.wrapperResizeObserver.observe(this.wrapper)),this.contentResizeObserver=new ResizeObserver(this.debouncedResize),this.contentResizeObserver.observe(this.content)),this.resize()}destroy(){var t,i;null===(t=this.wrapperResizeObserver)||void 0===t||t.disconnect(),null===(i=this.contentResizeObserver)||void 0===i||i.disconnect(),this.wrapper===window&&this.debouncedResize&&window.removeEventListener("resize",this.debouncedResize,!1)}get limit(){return{x:this.scrollWidth-this.width,y:this.scrollHeight-this.height}}}class Emitter{constructor(){this.events={}}emit(t,...i){var e;let s=this.events[t]||[];for(let t=0,o=s.length;t<o;t++)null===(e=s[t])||void 0===e||e.call(s,...i)}on(t,i){var e;return(null===(e=this.events[t])||void 0===e?void 0:e.push(i))||(this.events[t]=[i]),()=>{var e;this.events[t]=null===(e=this.events[t])||void 0===e?void 0:e.filter((t=>i!==t))}}off(t,i){var e;this.events[t]=null===(e=this.events[t])||void 0===e?void 0:e.filter((t=>i!==t))}destroy(){this.events={}}}const t=100/6,i={passive:!1};class VirtualScroll{constructor(e,s={wheelMultiplier:1,touchMultiplier:1}){this.element=e,this.options=s,this.touchStart={x:0,y:0},this.lastDelta={x:0,y:0},this.window={width:0,height:0},this.emitter=new Emitter,this.onTouchStart=t=>{const{clientX:i,clientY:e}=t.targetTouches?t.targetTouches[0]:t;this.touchStart.x=i,this.touchStart.y=e,this.lastDelta={x:0,y:0},this.emitter.emit("scroll",{deltaX:0,deltaY:0,event:t})},this.onTouchMove=t=>{const{clientX:i,clientY:e}=t.targetTouches?t.targetTouches[0]:t,s=-(i-this.touchStart.x)*this.options.touchMultiplier,o=-(e-this.touchStart.y)*this.options.touchMultiplier;this.touchStart.x=i,this.touchStart.y=e,this.lastDelta={x:s,y:o},this.emitter.emit("scroll",{deltaX:s,deltaY:o,event:t})},this.onTouchEnd=t=>{this.emitter.emit("scroll",{deltaX:this.lastDelta.x,deltaY:this.lastDelta.y,event:t})},this.onWheel=i=>{let{deltaX:e,deltaY:s,deltaMode:o}=i;e*=1===o?t:2===o?this.window.width:1,s*=1===o?t:2===o?this.window.height:1,e*=this.options.wheelMultiplier,s*=this.options.wheelMultiplier,this.emitter.emit("scroll",{deltaX:e,deltaY:s,event:i})},this.onWindowResize=()=>{this.window={width:window.innerWidth,height:window.innerHeight}},window.addEventListener("resize",this.onWindowResize,!1),this.onWindowResize(),this.element.addEventListener("wheel",this.onWheel,i),this.element.addEventListener("touchstart",this.onTouchStart,i),this.element.addEventListener("touchmove",this.onTouchMove,i),this.element.addEventListener("touchend",this.onTouchEnd,i)}on(t,i){return this.emitter.on(t,i)}destroy(){this.emitter.destroy(),window.removeEventListener("resize",this.onWindowResize,!1),this.element.removeEventListener("wheel",this.onWheel,i),this.element.removeEventListener("touchstart",this.onTouchStart,i),this.element.removeEventListener("touchmove",this.onTouchMove,i),this.element.removeEventListener("touchend",this.onTouchEnd,i)}}class Lenis{constructor({wrapper:t=window,content:i=document.documentElement,eventsTarget:e=t,smoothWheel:s=!0,syncTouch:o=!1,syncTouchLerp:n=.075,touchInertiaMultiplier:l=35,duration:r,easing:h=(t=>Math.min(1,1.001-Math.pow(2,-10*t))),lerp:a=.1,infinite:c=!1,orientation:u="vertical",gestureOrientation:d="vertical",touchMultiplier:p=1,wheelMultiplier:m=1,autoResize:v=!0,prevent:g,virtualScroll:S,__experimental__naiveDimensions:w=!1}={}){this._isScrolling=!1,this._isStopped=!1,this._isLocked=!1,this._preventNextNativeScrollEvent=!1,this._resetVelocityTimeout=null,this.time=0,this.userData={},this.lastVelocity=0,this.velocity=0,this.direction=0,this.animate=new Animate,this.emitter=new Emitter,this.onPointerDown=t=>{1===t.button&&this.reset()},this.onVirtualScroll=t=>{if("function"==typeof this.options.virtualScroll&&!1===this.options.virtualScroll(t))return;const{deltaX:i,deltaY:e,event:s}=t;if(this.emitter.emit("virtual-scroll",{deltaX:i,deltaY:e,event:s}),s.ctrlKey)return;const o=s.type.includes("touch"),n=s.type.includes("wheel");this.isTouching="touchstart"===s.type||"touchmove"===s.type;if(this.options.syncTouch&&o&&"touchstart"===s.type&&!this.isStopped&&!this.isLocked)return void this.reset();const l=0===i&&0===e,r="vertical"===this.options.gestureOrientation&&0===e||"horizontal"===this.options.gestureOrientation&&0===i;if(l||r)return;let h=s.composedPath();h=h.slice(0,h.indexOf(this.rootElement));const a=this.options.prevent;if(h.find((t=>{var i,e,s,l,r;return t instanceof HTMLElement&&("function"==typeof a&&(null==a?void 0:a(t))||(null===(i=t.hasAttribute)||void 0===i?void 0:i.call(t,"data-lenis-prevent"))||o&&(null===(e=t.hasAttribute)||void 0===e?void 0:e.call(t,"data-lenis-prevent-touch"))||n&&(null===(s=t.hasAttribute)||void 0===s?void 0:s.call(t,"data-lenis-prevent-wheel"))||(null===(l=t.classList)||void 0===l?void 0:l.contains("lenis"))&&!(null===(r=t.classList)||void 0===r?void 0:r.contains("lenis-stopped")))})))return;if(this.isStopped||this.isLocked)return void s.preventDefault();if(!(this.options.syncTouch&&o||this.options.smoothWheel&&n))return this.isScrolling="native",void this.animate.stop();s.preventDefault();let c=e;"both"===this.options.gestureOrientation?c=Math.abs(e)>Math.abs(i)?e:i:"horizontal"===this.options.gestureOrientation&&(c=i);const u=o&&this.options.syncTouch,d=o&&"touchend"===s.type&&Math.abs(c)>5;d&&(c=this.velocity*this.options.touchInertiaMultiplier),this.scrollTo(this.targetScroll+c,Object.assign({programmatic:!1},u?{lerp:d?this.options.syncTouchLerp:1}:{lerp:this.options.lerp,duration:this.options.duration,easing:this.options.easing}))},this.onNativeScroll=()=>{if(null!==this._resetVelocityTimeout&&(clearTimeout(this._resetVelocityTimeout),this._resetVelocityTimeout=null),this._preventNextNativeScrollEvent)this._preventNextNativeScrollEvent=!1;else if(!1===this.isScrolling||"native"===this.isScrolling){const t=this.animatedScroll;this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity,this.velocity=this.animatedScroll-t,this.direction=Math.sign(this.animatedScroll-t),this.isScrolling="native",this.emit(),0!==this.velocity&&(this._resetVelocityTimeout=setTimeout((()=>{this.lastVelocity=this.velocity,this.velocity=0,this.isScrolling=!1,this.emit()}),400))}},window.lenisVersion="1.1.13",t&&t!==document.documentElement&&t!==document.body||(t=window),this.options={wrapper:t,content:i,eventsTarget:e,smoothWheel:s,syncTouch:o,syncTouchLerp:n,touchInertiaMultiplier:l,duration:r,easing:h,lerp:a,infinite:c,gestureOrientation:d,orientation:u,touchMultiplier:p,wheelMultiplier:m,autoResize:v,prevent:g,virtualScroll:S,__experimental__naiveDimensions:w},this.dimensions=new Dimensions(t,i,{autoResize:v}),this.updateClassName(),this.targetScroll=this.animatedScroll=this.actualScroll,this.options.wrapper.addEventListener("scroll",this.onNativeScroll,!1),this.options.wrapper.addEventListener("pointerdown",this.onPointerDown,!1),this.virtualScroll=new VirtualScroll(e,{touchMultiplier:p,wheelMultiplier:m}),this.virtualScroll.on("scroll",this.onVirtualScroll)}destroy(){this.emitter.destroy(),this.options.wrapper.removeEventListener("scroll",this.onNativeScroll,!1),this.options.wrapper.removeEventListener("pointerdown",this.onPointerDown,!1),this.virtualScroll.destroy(),this.dimensions.destroy(),this.cleanUpClassName()}on(t,i){return this.emitter.on(t,i)}off(t,i){return this.emitter.off(t,i)}setScroll(t){this.isHorizontal?this.rootElement.scrollLeft=t:this.rootElement.scrollTop=t}resize(){this.dimensions.resize(),this.animatedScroll=this.targetScroll=this.actualScroll,this.emit()}emit(){this.emitter.emit("scroll",this)}reset(){this.isLocked=!1,this.isScrolling=!1,this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity=0,this.animate.stop()}start(){this.isStopped&&(this.isStopped=!1,this.reset())}stop(){this.isStopped||(this.isStopped=!0,this.animate.stop(),this.reset())}raf(t){const i=t-(this.time||t);this.time=t,this.animate.advance(.001*i)}scrollTo(t,{offset:i=0,immediate:e=!1,lock:s=!1,duration:o=this.options.duration,easing:n=this.options.easing,lerp:l=this.options.lerp,onStart:r,onComplete:h,force:a=!1,programmatic:c=!0,userData:u}={}){if(!this.isStopped&&!this.isLocked||a){if("string"==typeof t&&["top","left","start"].includes(t))t=0;else if("string"==typeof t&&["bottom","right","end"].includes(t))t=this.limit;else{let e;if("string"==typeof t?e=document.querySelector(t):t instanceof HTMLElement&&(null==t?void 0:t.nodeType)&&(e=t),e){if(this.options.wrapper!==window){const t=this.rootElement.getBoundingClientRect();i-=this.isHorizontal?t.left:t.top}const s=e.getBoundingClientRect();t=(this.isHorizontal?s.left:s.top)+this.animatedScroll}}if("number"==typeof t){if(t+=i,t=Math.round(t),this.options.infinite?c&&(this.targetScroll=this.animatedScroll=this.scroll):t=clamp(0,t,this.limit),t===this.targetScroll)return null==r||r(this),void(null==h||h(this));if(this.userData=null!=u?u:{},e)return this.animatedScroll=this.targetScroll=t,this.setScroll(this.scroll),this.reset(),this.preventNextNativeScrollEvent(),this.emit(),null==h||h(this),void(this.userData={});c||(this.targetScroll=t),this.animate.fromTo(this.animatedScroll,t,{duration:o,easing:n,lerp:l,onStart:()=>{s&&(this.isLocked=!0),this.isScrolling="smooth",null==r||r(this)},onUpdate:(t,i)=>{this.isScrolling="smooth",this.lastVelocity=this.velocity,this.velocity=t-this.animatedScroll,this.direction=Math.sign(this.velocity),this.animatedScroll=t,this.setScroll(this.scroll),c&&(this.targetScroll=t),i||this.emit(),i&&(this.reset(),this.emit(),null==h||h(this),this.userData={},this.preventNextNativeScrollEvent())}})}}}preventNextNativeScrollEvent(){this._preventNextNativeScrollEvent=!0,requestAnimationFrame((()=>{this._preventNextNativeScrollEvent=!1}))}get rootElement(){return this.options.wrapper===window?document.documentElement:this.options.wrapper}get limit(){return this.options.__experimental__naiveDimensions?this.isHorizontal?this.rootElement.scrollWidth-this.rootElement.clientWidth:this.rootElement.scrollHeight-this.rootElement.clientHeight:this.dimensions.limit[this.isHorizontal?"x":"y"]}get isHorizontal(){return"horizontal"===this.options.orientation}get actualScroll(){return this.isHorizontal?this.rootElement.scrollLeft:this.rootElement.scrollTop}get scroll(){return this.options.infinite?function modulo(t,i){return(t%i+i)%i}(this.animatedScroll,this.limit):this.animatedScroll}get progress(){return 0===this.limit?1:this.scroll/this.limit}get isScrolling(){return this._isScrolling}set isScrolling(t){this._isScrolling!==t&&(this._isScrolling=t,this.updateClassName())}get isStopped(){return this._isStopped}set isStopped(t){this._isStopped!==t&&(this._isStopped=t,this.updateClassName())}get isLocked(){return this._isLocked}set isLocked(t){this._isLocked!==t&&(this._isLocked=t,this.updateClassName())}get isSmooth(){return"smooth"===this.isScrolling}get className(){let t="lenis";return this.isStopped&&(t+=" lenis-stopped"),this.isLocked&&(t+=" lenis-locked"),this.isScrolling&&(t+=" lenis-scrolling"),"smooth"===this.isScrolling&&(t+=" lenis-smooth"),t}updateClassName(){this.cleanUpClassName(),this.rootElement.className=`${this.rootElement.className} ${this.className}`.trim()}cleanUpClassName(){this.rootElement.className=this.rootElement.className.replace(/lenis(-\w+)?/g,"").trim()}}export{Lenis as default};
2
- //# sourceMappingURL=lenis.mjs.map
1
+ // package.json
2
+ var version = "1.1.14-dev.3";
3
+
4
+ // packages/core/src/maths.ts
5
+ function clamp(min, input, max) {
6
+ return Math.max(min, Math.min(input, max));
7
+ }
8
+ function lerp(x, y, t) {
9
+ return (1 - t) * x + t * y;
10
+ }
11
+ function damp(x, y, lambda, deltaTime) {
12
+ return lerp(x, y, 1 - Math.exp(-lambda * deltaTime));
13
+ }
14
+ function modulo(n, d) {
15
+ return (n % d + d) % d;
16
+ }
17
+
18
+ // packages/core/src/animate.ts
19
+ var Animate = class {
20
+ isRunning = false;
21
+ value = 0;
22
+ from = 0;
23
+ to = 0;
24
+ currentTime = 0;
25
+ // These are instanciated in the fromTo method
26
+ lerp;
27
+ duration;
28
+ easing;
29
+ onUpdate;
30
+ /**
31
+ * Advance the animation by the given delta time
32
+ *
33
+ * @param deltaTime - The time in seconds to advance the animation
34
+ */
35
+ advance(deltaTime) {
36
+ if (!this.isRunning) return;
37
+ let completed = false;
38
+ if (this.duration && this.easing) {
39
+ this.currentTime += deltaTime;
40
+ const linearProgress = clamp(0, this.currentTime / this.duration, 1);
41
+ completed = linearProgress >= 1;
42
+ const easedProgress = completed ? 1 : this.easing(linearProgress);
43
+ this.value = this.from + (this.to - this.from) * easedProgress;
44
+ } else if (this.lerp) {
45
+ this.value = damp(this.value, this.to, this.lerp * 60, deltaTime);
46
+ if (Math.round(this.value) === this.to) {
47
+ this.value = this.to;
48
+ completed = true;
49
+ }
50
+ } else {
51
+ this.value = this.to;
52
+ completed = true;
53
+ }
54
+ if (completed) {
55
+ this.stop();
56
+ }
57
+ this.onUpdate?.(this.value, completed);
58
+ }
59
+ /** Stop the animation */
60
+ stop() {
61
+ this.isRunning = false;
62
+ }
63
+ /**
64
+ * Set up the animation from a starting value to an ending value
65
+ * with optional parameters for lerping, duration, easing, and onUpdate callback
66
+ *
67
+ * @param from - The starting value
68
+ * @param to - The ending value
69
+ * @param options - Options for the animation
70
+ */
71
+ fromTo(from, to, { lerp: lerp2, duration, easing, onStart, onUpdate }) {
72
+ this.from = this.value = from;
73
+ this.to = to;
74
+ this.lerp = lerp2;
75
+ this.duration = duration;
76
+ this.easing = easing;
77
+ this.currentTime = 0;
78
+ this.isRunning = true;
79
+ onStart?.();
80
+ this.onUpdate = onUpdate;
81
+ }
82
+ };
83
+
84
+ // packages/core/src/debounce.ts
85
+ function debounce(callback, delay) {
86
+ let timer;
87
+ return function(...args) {
88
+ let context = this;
89
+ clearTimeout(timer);
90
+ timer = setTimeout(() => {
91
+ timer = void 0;
92
+ callback.apply(context, args);
93
+ }, delay);
94
+ };
95
+ }
96
+
97
+ // packages/core/src/dimensions.ts
98
+ var Dimensions = class {
99
+ constructor(wrapper, content, { autoResize = true, debounce: debounceValue = 250 } = {}) {
100
+ this.wrapper = wrapper;
101
+ this.content = content;
102
+ if (autoResize) {
103
+ this.debouncedResize = debounce(this.resize, debounceValue);
104
+ if (this.wrapper instanceof Window) {
105
+ window.addEventListener("resize", this.debouncedResize, false);
106
+ } else {
107
+ this.wrapperResizeObserver = new ResizeObserver(this.debouncedResize);
108
+ this.wrapperResizeObserver.observe(this.wrapper);
109
+ }
110
+ this.contentResizeObserver = new ResizeObserver(this.debouncedResize);
111
+ this.contentResizeObserver.observe(this.content);
112
+ }
113
+ this.resize();
114
+ }
115
+ width = 0;
116
+ height = 0;
117
+ scrollHeight = 0;
118
+ scrollWidth = 0;
119
+ // These are instanciated in the constructor as they need information from the options
120
+ debouncedResize;
121
+ wrapperResizeObserver;
122
+ contentResizeObserver;
123
+ destroy() {
124
+ this.wrapperResizeObserver?.disconnect();
125
+ this.contentResizeObserver?.disconnect();
126
+ if (this.wrapper === window && this.debouncedResize) {
127
+ window.removeEventListener("resize", this.debouncedResize, false);
128
+ }
129
+ }
130
+ resize = () => {
131
+ this.onWrapperResize();
132
+ this.onContentResize();
133
+ };
134
+ onWrapperResize = () => {
135
+ if (this.wrapper instanceof Window) {
136
+ this.width = window.innerWidth;
137
+ this.height = window.innerHeight;
138
+ } else {
139
+ this.width = this.wrapper.clientWidth;
140
+ this.height = this.wrapper.clientHeight;
141
+ }
142
+ };
143
+ onContentResize = () => {
144
+ if (this.wrapper instanceof Window) {
145
+ this.scrollHeight = this.content.scrollHeight;
146
+ this.scrollWidth = this.content.scrollWidth;
147
+ } else {
148
+ this.scrollHeight = this.wrapper.scrollHeight;
149
+ this.scrollWidth = this.wrapper.scrollWidth;
150
+ }
151
+ };
152
+ get limit() {
153
+ return {
154
+ x: this.scrollWidth - this.width,
155
+ y: this.scrollHeight - this.height
156
+ };
157
+ }
158
+ };
159
+
160
+ // packages/core/src/emitter.ts
161
+ var Emitter = class {
162
+ events = {};
163
+ /**
164
+ * Emit an event with the given data
165
+ * @param event Event name
166
+ * @param args Data to pass to the event handlers
167
+ */
168
+ emit(event, ...args) {
169
+ let callbacks = this.events[event] || [];
170
+ for (let i = 0, length = callbacks.length; i < length; i++) {
171
+ callbacks[i]?.(...args);
172
+ }
173
+ }
174
+ /**
175
+ * Add a callback to the event
176
+ * @param event Event name
177
+ * @param cb Callback function
178
+ * @returns Unsubscribe function
179
+ */
180
+ on(event, cb) {
181
+ this.events[event]?.push(cb) || (this.events[event] = [cb]);
182
+ return () => {
183
+ this.events[event] = this.events[event]?.filter((i) => cb !== i);
184
+ };
185
+ }
186
+ /**
187
+ * Remove a callback from the event
188
+ * @param event Event name
189
+ * @param callback Callback function
190
+ */
191
+ off(event, callback) {
192
+ this.events[event] = this.events[event]?.filter((i) => callback !== i);
193
+ }
194
+ /**
195
+ * Remove all event listeners and clean up
196
+ */
197
+ destroy() {
198
+ this.events = {};
199
+ }
200
+ };
201
+
202
+ // packages/core/src/virtual-scroll.ts
203
+ var LINE_HEIGHT = 100 / 6;
204
+ var listenerOptions = { passive: false };
205
+ var VirtualScroll = class {
206
+ constructor(element, options = { wheelMultiplier: 1, touchMultiplier: 1 }) {
207
+ this.element = element;
208
+ this.options = options;
209
+ window.addEventListener("resize", this.onWindowResize, false);
210
+ this.onWindowResize();
211
+ this.element.addEventListener("wheel", this.onWheel, listenerOptions);
212
+ this.element.addEventListener(
213
+ "touchstart",
214
+ this.onTouchStart,
215
+ listenerOptions
216
+ );
217
+ this.element.addEventListener(
218
+ "touchmove",
219
+ this.onTouchMove,
220
+ listenerOptions
221
+ );
222
+ this.element.addEventListener("touchend", this.onTouchEnd, listenerOptions);
223
+ }
224
+ touchStart = {
225
+ x: 0,
226
+ y: 0
227
+ };
228
+ lastDelta = {
229
+ x: 0,
230
+ y: 0
231
+ };
232
+ window = {
233
+ width: 0,
234
+ height: 0
235
+ };
236
+ emitter = new Emitter();
237
+ /**
238
+ * Add an event listener for the given event and callback
239
+ *
240
+ * @param event Event name
241
+ * @param callback Callback function
242
+ */
243
+ on(event, callback) {
244
+ return this.emitter.on(event, callback);
245
+ }
246
+ /** Remove all event listeners and clean up */
247
+ destroy() {
248
+ this.emitter.destroy();
249
+ window.removeEventListener("resize", this.onWindowResize, false);
250
+ this.element.removeEventListener("wheel", this.onWheel, listenerOptions);
251
+ this.element.removeEventListener(
252
+ "touchstart",
253
+ this.onTouchStart,
254
+ listenerOptions
255
+ );
256
+ this.element.removeEventListener(
257
+ "touchmove",
258
+ this.onTouchMove,
259
+ listenerOptions
260
+ );
261
+ this.element.removeEventListener(
262
+ "touchend",
263
+ this.onTouchEnd,
264
+ listenerOptions
265
+ );
266
+ }
267
+ /**
268
+ * Event handler for 'touchstart' event
269
+ *
270
+ * @param event Touch event
271
+ */
272
+ onTouchStart = (event) => {
273
+ const { clientX, clientY } = event.targetTouches ? event.targetTouches[0] : event;
274
+ this.touchStart.x = clientX;
275
+ this.touchStart.y = clientY;
276
+ this.lastDelta = {
277
+ x: 0,
278
+ y: 0
279
+ };
280
+ this.emitter.emit("scroll", {
281
+ deltaX: 0,
282
+ deltaY: 0,
283
+ event
284
+ });
285
+ };
286
+ /** Event handler for 'touchmove' event */
287
+ onTouchMove = (event) => {
288
+ const { clientX, clientY } = event.targetTouches ? event.targetTouches[0] : event;
289
+ const deltaX = -(clientX - this.touchStart.x) * this.options.touchMultiplier;
290
+ const deltaY = -(clientY - this.touchStart.y) * this.options.touchMultiplier;
291
+ this.touchStart.x = clientX;
292
+ this.touchStart.y = clientY;
293
+ this.lastDelta = {
294
+ x: deltaX,
295
+ y: deltaY
296
+ };
297
+ this.emitter.emit("scroll", {
298
+ deltaX,
299
+ deltaY,
300
+ event
301
+ });
302
+ };
303
+ onTouchEnd = (event) => {
304
+ this.emitter.emit("scroll", {
305
+ deltaX: this.lastDelta.x,
306
+ deltaY: this.lastDelta.y,
307
+ event
308
+ });
309
+ };
310
+ /** Event handler for 'wheel' event */
311
+ onWheel = (event) => {
312
+ let { deltaX, deltaY, deltaMode } = event;
313
+ const multiplierX = deltaMode === 1 ? LINE_HEIGHT : deltaMode === 2 ? this.window.width : 1;
314
+ const multiplierY = deltaMode === 1 ? LINE_HEIGHT : deltaMode === 2 ? this.window.height : 1;
315
+ deltaX *= multiplierX;
316
+ deltaY *= multiplierY;
317
+ deltaX *= this.options.wheelMultiplier;
318
+ deltaY *= this.options.wheelMultiplier;
319
+ this.emitter.emit("scroll", { deltaX, deltaY, event });
320
+ };
321
+ onWindowResize = () => {
322
+ this.window = {
323
+ width: window.innerWidth,
324
+ height: window.innerHeight
325
+ };
326
+ };
327
+ };
328
+
329
+ // packages/core/src/lenis.ts
330
+ var Lenis = class {
331
+ _isScrolling = false;
332
+ // true when scroll is animating
333
+ _isStopped = false;
334
+ // true if user should not be able to scroll - enable/disable programmatically
335
+ _isLocked = false;
336
+ // same as isStopped but enabled/disabled when scroll reaches target
337
+ _preventNextNativeScrollEvent = false;
338
+ _resetVelocityTimeout = null;
339
+ /**
340
+ * Whether or not the user is touching the screen
341
+ */
342
+ isTouching;
343
+ /**
344
+ * The time in ms since the lenis instance was created
345
+ */
346
+ time = 0;
347
+ /**
348
+ * User data that will be forwarded through the scroll event
349
+ *
350
+ * @example
351
+ * lenis.scrollTo(100, {
352
+ * userData: {
353
+ * foo: 'bar'
354
+ * }
355
+ * })
356
+ */
357
+ userData = {};
358
+ /**
359
+ * The last velocity of the scroll
360
+ */
361
+ lastVelocity = 0;
362
+ /**
363
+ * The current velocity of the scroll
364
+ */
365
+ velocity = 0;
366
+ /**
367
+ * The direction of the scroll
368
+ */
369
+ direction = 0;
370
+ /**
371
+ * The options passed to the lenis instance
372
+ */
373
+ options;
374
+ /**
375
+ * The target scroll value
376
+ */
377
+ targetScroll;
378
+ /**
379
+ * The animated scroll value
380
+ */
381
+ animatedScroll;
382
+ // These are instanciated here as they don't need information from the options
383
+ animate = new Animate();
384
+ emitter = new Emitter();
385
+ // These are instanciated in the constructor as they need information from the options
386
+ dimensions;
387
+ // This is not private because it's used in the Snap class
388
+ virtualScroll;
389
+ constructor({
390
+ wrapper = window,
391
+ content = document.documentElement,
392
+ eventsTarget = wrapper,
393
+ smoothWheel = true,
394
+ syncTouch = false,
395
+ syncTouchLerp = 0.075,
396
+ touchInertiaMultiplier = 35,
397
+ duration,
398
+ // in seconds
399
+ easing = (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
400
+ lerp: lerp2 = 0.1,
401
+ infinite = false,
402
+ orientation = "vertical",
403
+ // vertical, horizontal
404
+ gestureOrientation = "vertical",
405
+ // vertical, horizontal, both
406
+ touchMultiplier = 1,
407
+ wheelMultiplier = 1,
408
+ autoResize = true,
409
+ prevent,
410
+ virtualScroll,
411
+ __experimental__naiveDimensions = false
412
+ } = {}) {
413
+ window.lenisVersion = version;
414
+ if (!wrapper || wrapper === document.documentElement || wrapper === document.body) {
415
+ wrapper = window;
416
+ }
417
+ this.options = {
418
+ wrapper,
419
+ content,
420
+ eventsTarget,
421
+ smoothWheel,
422
+ syncTouch,
423
+ syncTouchLerp,
424
+ touchInertiaMultiplier,
425
+ duration,
426
+ easing,
427
+ lerp: lerp2,
428
+ infinite,
429
+ gestureOrientation,
430
+ orientation,
431
+ touchMultiplier,
432
+ wheelMultiplier,
433
+ autoResize,
434
+ prevent,
435
+ virtualScroll,
436
+ __experimental__naiveDimensions
437
+ };
438
+ this.dimensions = new Dimensions(wrapper, content, { autoResize });
439
+ this.updateClassName();
440
+ this.targetScroll = this.animatedScroll = this.actualScroll;
441
+ this.options.wrapper.addEventListener("scroll", this.onNativeScroll, false);
442
+ this.options.wrapper.addEventListener(
443
+ "pointerdown",
444
+ this.onPointerDown,
445
+ false
446
+ );
447
+ this.virtualScroll = new VirtualScroll(eventsTarget, {
448
+ touchMultiplier,
449
+ wheelMultiplier
450
+ });
451
+ this.virtualScroll.on("scroll", this.onVirtualScroll);
452
+ }
453
+ /**
454
+ * Destroy the lenis instance, remove all event listeners and clean up the class name
455
+ */
456
+ destroy() {
457
+ this.emitter.destroy();
458
+ this.options.wrapper.removeEventListener(
459
+ "scroll",
460
+ this.onNativeScroll,
461
+ false
462
+ );
463
+ this.options.wrapper.removeEventListener(
464
+ "pointerdown",
465
+ this.onPointerDown,
466
+ false
467
+ );
468
+ this.virtualScroll.destroy();
469
+ this.dimensions.destroy();
470
+ this.cleanUpClassName();
471
+ }
472
+ on(event, callback) {
473
+ return this.emitter.on(event, callback);
474
+ }
475
+ off(event, callback) {
476
+ return this.emitter.off(event, callback);
477
+ }
478
+ setScroll(scroll) {
479
+ if (this.isHorizontal) {
480
+ this.rootElement.scrollLeft = scroll;
481
+ } else {
482
+ this.rootElement.scrollTop = scroll;
483
+ }
484
+ }
485
+ onPointerDown = (event) => {
486
+ if (event.button === 1) {
487
+ this.reset();
488
+ }
489
+ };
490
+ onVirtualScroll = (data) => {
491
+ if (typeof this.options.virtualScroll === "function" && this.options.virtualScroll(data) === false)
492
+ return;
493
+ const { deltaX, deltaY, event } = data;
494
+ this.emitter.emit("virtual-scroll", { deltaX, deltaY, event });
495
+ if (event.ctrlKey) return;
496
+ const isTouch = event.type.includes("touch");
497
+ const isWheel = event.type.includes("wheel");
498
+ this.isTouching = event.type === "touchstart" || event.type === "touchmove";
499
+ const isTapToStop = this.options.syncTouch && isTouch && event.type === "touchstart" && !this.isStopped && !this.isLocked;
500
+ if (isTapToStop) {
501
+ this.reset();
502
+ return;
503
+ }
504
+ const isClick = deltaX === 0 && deltaY === 0;
505
+ const isUnknownGesture = this.options.gestureOrientation === "vertical" && deltaY === 0 || this.options.gestureOrientation === "horizontal" && deltaX === 0;
506
+ if (isClick || isUnknownGesture) {
507
+ return;
508
+ }
509
+ let composedPath = event.composedPath();
510
+ composedPath = composedPath.slice(0, composedPath.indexOf(this.rootElement));
511
+ const prevent = this.options.prevent;
512
+ if (!!composedPath.find(
513
+ (node) => node instanceof HTMLElement && (typeof prevent === "function" && prevent?.(node) || node.hasAttribute?.("data-lenis-prevent") || isTouch && node.hasAttribute?.("data-lenis-prevent-touch") || isWheel && node.hasAttribute?.("data-lenis-prevent-wheel") || node.classList?.contains("lenis") && !node.classList?.contains("lenis-stopped"))
514
+ // nested lenis instance
515
+ ))
516
+ return;
517
+ if (this.isStopped || this.isLocked) {
518
+ event.preventDefault();
519
+ return;
520
+ }
521
+ const isSmooth = this.options.syncTouch && isTouch || this.options.smoothWheel && isWheel;
522
+ if (!isSmooth) {
523
+ this.isScrolling = "native";
524
+ this.animate.stop();
525
+ return;
526
+ }
527
+ event.preventDefault();
528
+ let delta = deltaY;
529
+ if (this.options.gestureOrientation === "both") {
530
+ delta = Math.abs(deltaY) > Math.abs(deltaX) ? deltaY : deltaX;
531
+ } else if (this.options.gestureOrientation === "horizontal") {
532
+ delta = deltaX;
533
+ }
534
+ const syncTouch = isTouch && this.options.syncTouch;
535
+ const isTouchEnd = isTouch && event.type === "touchend";
536
+ const hasTouchInertia = isTouchEnd && Math.abs(delta) > 5;
537
+ if (hasTouchInertia) {
538
+ delta = this.velocity * this.options.touchInertiaMultiplier;
539
+ }
540
+ this.scrollTo(this.targetScroll + delta, {
541
+ programmatic: false,
542
+ ...syncTouch ? {
543
+ lerp: hasTouchInertia ? this.options.syncTouchLerp : 1
544
+ } : {
545
+ lerp: this.options.lerp,
546
+ duration: this.options.duration,
547
+ easing: this.options.easing
548
+ }
549
+ });
550
+ };
551
+ /**
552
+ * Force lenis to recalculate the dimensions
553
+ */
554
+ resize() {
555
+ this.dimensions.resize();
556
+ this.animatedScroll = this.targetScroll = this.actualScroll;
557
+ this.emit();
558
+ }
559
+ emit() {
560
+ this.emitter.emit("scroll", this);
561
+ }
562
+ onNativeScroll = () => {
563
+ if (this._resetVelocityTimeout !== null) {
564
+ clearTimeout(this._resetVelocityTimeout);
565
+ this._resetVelocityTimeout = null;
566
+ }
567
+ if (this._preventNextNativeScrollEvent) {
568
+ this._preventNextNativeScrollEvent = false;
569
+ return;
570
+ }
571
+ if (this.isScrolling === false || this.isScrolling === "native") {
572
+ const lastScroll = this.animatedScroll;
573
+ this.animatedScroll = this.targetScroll = this.actualScroll;
574
+ this.lastVelocity = this.velocity;
575
+ this.velocity = this.animatedScroll - lastScroll;
576
+ this.direction = Math.sign(
577
+ this.animatedScroll - lastScroll
578
+ );
579
+ this.isScrolling = "native";
580
+ this.emit();
581
+ if (this.velocity !== 0) {
582
+ this._resetVelocityTimeout = setTimeout(() => {
583
+ this.lastVelocity = this.velocity;
584
+ this.velocity = 0;
585
+ this.isScrolling = false;
586
+ this.emit();
587
+ }, 400);
588
+ }
589
+ }
590
+ };
591
+ reset() {
592
+ this.isLocked = false;
593
+ this.isScrolling = false;
594
+ this.animatedScroll = this.targetScroll = this.actualScroll;
595
+ this.lastVelocity = this.velocity = 0;
596
+ this.animate.stop();
597
+ }
598
+ /**
599
+ * Start lenis scroll after it has been stopped
600
+ */
601
+ start() {
602
+ if (!this.isStopped) return;
603
+ this.isStopped = false;
604
+ this.reset();
605
+ }
606
+ /**
607
+ * Stop lenis scroll
608
+ */
609
+ stop() {
610
+ if (this.isStopped) return;
611
+ this.isStopped = true;
612
+ this.animate.stop();
613
+ this.reset();
614
+ }
615
+ /**
616
+ * RequestAnimationFrame for lenis
617
+ *
618
+ * @param time The time in ms from an external clock like `requestAnimationFrame` or Tempus
619
+ */
620
+ raf(time) {
621
+ const deltaTime = time - (this.time || time);
622
+ this.time = time;
623
+ this.animate.advance(deltaTime * 1e-3);
624
+ }
625
+ /**
626
+ * Scroll to a target value
627
+ *
628
+ * @param target The target value to scroll to
629
+ * @param options The options for the scroll
630
+ *
631
+ * @example
632
+ * lenis.scrollTo(100, {
633
+ * offset: 100,
634
+ * duration: 1,
635
+ * easing: (t) => 1 - Math.cos((t * Math.PI) / 2),
636
+ * lerp: 0.1,
637
+ * onStart: () => {
638
+ * console.log('onStart')
639
+ * },
640
+ * onComplete: () => {
641
+ * console.log('onComplete')
642
+ * },
643
+ * })
644
+ */
645
+ scrollTo(target, {
646
+ offset = 0,
647
+ immediate = false,
648
+ lock = false,
649
+ duration = this.options.duration,
650
+ easing = this.options.easing,
651
+ lerp: lerp2 = this.options.lerp,
652
+ onStart,
653
+ onComplete,
654
+ force = false,
655
+ // scroll even if stopped
656
+ programmatic = true,
657
+ // called from outside of the class
658
+ userData
659
+ } = {}) {
660
+ if ((this.isStopped || this.isLocked) && !force) return;
661
+ if (typeof target === "string" && ["top", "left", "start"].includes(target)) {
662
+ target = 0;
663
+ } else if (typeof target === "string" && ["bottom", "right", "end"].includes(target)) {
664
+ target = this.limit;
665
+ } else {
666
+ let node;
667
+ if (typeof target === "string") {
668
+ node = document.querySelector(target);
669
+ } else if (target instanceof HTMLElement && target?.nodeType) {
670
+ node = target;
671
+ }
672
+ if (node) {
673
+ if (this.options.wrapper !== window) {
674
+ const wrapperRect = this.rootElement.getBoundingClientRect();
675
+ offset -= this.isHorizontal ? wrapperRect.left : wrapperRect.top;
676
+ }
677
+ const rect = node.getBoundingClientRect();
678
+ target = (this.isHorizontal ? rect.left : rect.top) + this.animatedScroll;
679
+ }
680
+ }
681
+ if (typeof target !== "number") return;
682
+ target += offset;
683
+ target = Math.round(target);
684
+ if (this.options.infinite) {
685
+ if (programmatic) {
686
+ this.targetScroll = this.animatedScroll = this.scroll;
687
+ }
688
+ } else {
689
+ target = clamp(0, target, this.limit);
690
+ }
691
+ if (target === this.targetScroll) {
692
+ onStart?.(this);
693
+ onComplete?.(this);
694
+ return;
695
+ }
696
+ this.userData = userData ?? {};
697
+ if (immediate) {
698
+ this.animatedScroll = this.targetScroll = target;
699
+ this.setScroll(this.scroll);
700
+ this.reset();
701
+ this.preventNextNativeScrollEvent();
702
+ this.emit();
703
+ onComplete?.(this);
704
+ this.userData = {};
705
+ return;
706
+ }
707
+ if (!programmatic) {
708
+ this.targetScroll = target;
709
+ }
710
+ this.animate.fromTo(this.animatedScroll, target, {
711
+ duration,
712
+ easing,
713
+ lerp: lerp2,
714
+ onStart: () => {
715
+ if (lock) this.isLocked = true;
716
+ this.isScrolling = "smooth";
717
+ onStart?.(this);
718
+ },
719
+ onUpdate: (value, completed) => {
720
+ this.isScrolling = "smooth";
721
+ this.lastVelocity = this.velocity;
722
+ this.velocity = value - this.animatedScroll;
723
+ this.direction = Math.sign(this.velocity);
724
+ this.animatedScroll = value;
725
+ this.setScroll(this.scroll);
726
+ if (programmatic) {
727
+ this.targetScroll = value;
728
+ }
729
+ if (!completed) this.emit();
730
+ if (completed) {
731
+ this.reset();
732
+ this.emit();
733
+ onComplete?.(this);
734
+ this.userData = {};
735
+ this.preventNextNativeScrollEvent();
736
+ }
737
+ }
738
+ });
739
+ }
740
+ preventNextNativeScrollEvent() {
741
+ this._preventNextNativeScrollEvent = true;
742
+ requestAnimationFrame(() => {
743
+ this._preventNextNativeScrollEvent = false;
744
+ });
745
+ }
746
+ /**
747
+ * The root element on which lenis is instanced
748
+ */
749
+ get rootElement() {
750
+ return this.options.wrapper === window ? document.documentElement : this.options.wrapper;
751
+ }
752
+ /**
753
+ * The limit which is the maximum scroll value
754
+ */
755
+ get limit() {
756
+ if (this.options.__experimental__naiveDimensions) {
757
+ if (this.isHorizontal) {
758
+ return this.rootElement.scrollWidth - this.rootElement.clientWidth;
759
+ } else {
760
+ return this.rootElement.scrollHeight - this.rootElement.clientHeight;
761
+ }
762
+ } else {
763
+ return this.dimensions.limit[this.isHorizontal ? "x" : "y"];
764
+ }
765
+ }
766
+ /**
767
+ * Whether or not the scroll is horizontal
768
+ */
769
+ get isHorizontal() {
770
+ return this.options.orientation === "horizontal";
771
+ }
772
+ /**
773
+ * The actual scroll value
774
+ */
775
+ get actualScroll() {
776
+ return this.isHorizontal ? this.rootElement.scrollLeft : this.rootElement.scrollTop;
777
+ }
778
+ /**
779
+ * The current scroll value
780
+ */
781
+ get scroll() {
782
+ return this.options.infinite ? modulo(this.animatedScroll, this.limit) : this.animatedScroll;
783
+ }
784
+ /**
785
+ * The progress of the scroll relative to the limit
786
+ */
787
+ get progress() {
788
+ return this.limit === 0 ? 1 : this.scroll / this.limit;
789
+ }
790
+ /**
791
+ * Current scroll state
792
+ */
793
+ get isScrolling() {
794
+ return this._isScrolling;
795
+ }
796
+ set isScrolling(value) {
797
+ if (this._isScrolling !== value) {
798
+ this._isScrolling = value;
799
+ this.updateClassName();
800
+ }
801
+ }
802
+ /**
803
+ * Check if lenis is stopped
804
+ */
805
+ get isStopped() {
806
+ return this._isStopped;
807
+ }
808
+ set isStopped(value) {
809
+ if (this._isStopped !== value) {
810
+ this._isStopped = value;
811
+ this.updateClassName();
812
+ }
813
+ }
814
+ /**
815
+ * Check if lenis is locked
816
+ */
817
+ get isLocked() {
818
+ return this._isLocked;
819
+ }
820
+ set isLocked(value) {
821
+ if (this._isLocked !== value) {
822
+ this._isLocked = value;
823
+ this.updateClassName();
824
+ }
825
+ }
826
+ /**
827
+ * Check if lenis is smooth scrolling
828
+ */
829
+ get isSmooth() {
830
+ return this.isScrolling === "smooth";
831
+ }
832
+ /**
833
+ * The class name applied to the wrapper element
834
+ */
835
+ get className() {
836
+ let className = "lenis";
837
+ if (this.isStopped) className += " lenis-stopped";
838
+ if (this.isLocked) className += " lenis-locked";
839
+ if (this.isScrolling) className += " lenis-scrolling";
840
+ if (this.isScrolling === "smooth") className += " lenis-smooth";
841
+ return className;
842
+ }
843
+ updateClassName() {
844
+ this.cleanUpClassName();
845
+ this.rootElement.className = `${this.rootElement.className} ${this.className}`.trim();
846
+ }
847
+ cleanUpClassName() {
848
+ this.rootElement.className = this.rootElement.className.replace(/lenis(-\w+)?/g, "").trim();
849
+ }
850
+ };
851
+ export {
852
+ Lenis as default
853
+ };
854
+ //# sourceMappingURL=lenis.mjs.map