lenis 1.3.19 → 1.3.20-dev.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.
package/dist/lenis.js CHANGED
@@ -1,1132 +1,1030 @@
1
- // package.json
2
- var version = "1.3.19";
1
+ (function() {
3
2
 
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
3
 
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
- };
4
+ //#region package.json
5
+ var version = "1.3.20-dev.0";
83
6
 
84
- // packages/core/src/debounce.ts
85
- function debounce(callback, delay) {
86
- let timer;
87
- return function(...args) {
88
- clearTimeout(timer);
89
- timer = setTimeout(() => {
90
- timer = void 0;
91
- callback.apply(this, args);
92
- }, delay);
93
- };
94
- }
7
+ //#endregion
8
+ //#region packages/core/src/maths.ts
9
+ /**
10
+ * Clamp a value between a minimum and maximum value
11
+ *
12
+ * @param min Minimum value
13
+ * @param input Value to clamp
14
+ * @param max Maximum value
15
+ * @returns Clamped value
16
+ */
17
+ function clamp(min, input, max) {
18
+ return Math.max(min, Math.min(input, max));
19
+ }
20
+ /**
21
+ * Linearly interpolate between two values using an amount (0 <= t <= 1)
22
+ *
23
+ * @param x First value
24
+ * @param y Second value
25
+ * @param t Amount to interpolate (0 <= t <= 1)
26
+ * @returns Interpolated value
27
+ */
28
+ function lerp(x, y, t) {
29
+ return (1 - t) * x + t * y;
30
+ }
31
+ /**
32
+ * Damp a value over time using a damping factor
33
+ * {@link http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/}
34
+ *
35
+ * @param x Initial value
36
+ * @param y Target value
37
+ * @param lambda Damping factor
38
+ * @param dt Time elapsed since the last update
39
+ * @returns Damped value
40
+ */
41
+ function damp(x, y, lambda, deltaTime) {
42
+ return lerp(x, y, 1 - Math.exp(-lambda * deltaTime));
43
+ }
44
+ /**
45
+ * Calculate the modulo of the dividend and divisor while keeping the result within the same sign as the divisor
46
+ * {@link https://anguscroll.com/just/just-modulo}
47
+ *
48
+ * @param n Dividend
49
+ * @param d Divisor
50
+ * @returns Modulo
51
+ */
52
+ function modulo(n, d) {
53
+ return (n % d + d) % d;
54
+ }
95
55
 
96
- // packages/core/src/dimensions.ts
97
- var Dimensions = class {
98
- constructor(wrapper, content, { autoResize = true, debounce: debounceValue = 250 } = {}) {
99
- this.wrapper = wrapper;
100
- this.content = content;
101
- if (autoResize) {
102
- this.debouncedResize = debounce(this.resize, debounceValue);
103
- if (this.wrapper instanceof Window) {
104
- window.addEventListener("resize", this.debouncedResize);
105
- } else {
106
- this.wrapperResizeObserver = new ResizeObserver(this.debouncedResize);
107
- this.wrapperResizeObserver.observe(this.wrapper);
108
- }
109
- this.contentResizeObserver = new ResizeObserver(this.debouncedResize);
110
- this.contentResizeObserver.observe(this.content);
111
- }
112
- this.resize();
113
- }
114
- width = 0;
115
- height = 0;
116
- scrollHeight = 0;
117
- scrollWidth = 0;
118
- // These are instanciated in the constructor as they need information from the options
119
- debouncedResize;
120
- wrapperResizeObserver;
121
- contentResizeObserver;
122
- destroy() {
123
- this.wrapperResizeObserver?.disconnect();
124
- this.contentResizeObserver?.disconnect();
125
- if (this.wrapper === window && this.debouncedResize) {
126
- window.removeEventListener("resize", this.debouncedResize);
127
- }
128
- }
129
- resize = () => {
130
- this.onWrapperResize();
131
- this.onContentResize();
132
- };
133
- onWrapperResize = () => {
134
- if (this.wrapper instanceof Window) {
135
- this.width = window.innerWidth;
136
- this.height = window.innerHeight;
137
- } else {
138
- this.width = this.wrapper.clientWidth;
139
- this.height = this.wrapper.clientHeight;
140
- }
141
- };
142
- onContentResize = () => {
143
- if (this.wrapper instanceof Window) {
144
- this.scrollHeight = this.content.scrollHeight;
145
- this.scrollWidth = this.content.scrollWidth;
146
- } else {
147
- this.scrollHeight = this.wrapper.scrollHeight;
148
- this.scrollWidth = this.wrapper.scrollWidth;
149
- }
150
- };
151
- get limit() {
152
- return {
153
- x: this.scrollWidth - this.width,
154
- y: this.scrollHeight - this.height
155
- };
156
- }
157
- };
56
+ //#endregion
57
+ //#region packages/core/src/animate.ts
58
+ /**
59
+ * Animate class to handle value animations with lerping or easing
60
+ *
61
+ * @example
62
+ * const animate = new Animate()
63
+ * animate.fromTo(0, 100, { duration: 1, easing: (t) => t })
64
+ * animate.advance(0.5) // 50
65
+ */
66
+ var Animate = class {
67
+ isRunning = false;
68
+ value = 0;
69
+ from = 0;
70
+ to = 0;
71
+ currentTime = 0;
72
+ lerp;
73
+ duration;
74
+ easing;
75
+ onUpdate;
76
+ /**
77
+ * Advance the animation by the given delta time
78
+ *
79
+ * @param deltaTime - The time in seconds to advance the animation
80
+ */
81
+ advance(deltaTime) {
82
+ if (!this.isRunning) return;
83
+ let completed = false;
84
+ if (this.duration && this.easing) {
85
+ this.currentTime += deltaTime;
86
+ const linearProgress = clamp(0, this.currentTime / this.duration, 1);
87
+ completed = linearProgress >= 1;
88
+ const easedProgress = completed ? 1 : this.easing(linearProgress);
89
+ this.value = this.from + (this.to - this.from) * easedProgress;
90
+ } else if (this.lerp) {
91
+ this.value = damp(this.value, this.to, this.lerp * 60, deltaTime);
92
+ if (Math.round(this.value) === this.to) {
93
+ this.value = this.to;
94
+ completed = true;
95
+ }
96
+ } else {
97
+ this.value = this.to;
98
+ completed = true;
99
+ }
100
+ if (completed) this.stop();
101
+ this.onUpdate?.(this.value, completed);
102
+ }
103
+ /** Stop the animation */
104
+ stop() {
105
+ this.isRunning = false;
106
+ }
107
+ /**
108
+ * Set up the animation from a starting value to an ending value
109
+ * with optional parameters for lerping, duration, easing, and onUpdate callback
110
+ *
111
+ * @param from - The starting value
112
+ * @param to - The ending value
113
+ * @param options - Options for the animation
114
+ */
115
+ fromTo(from, to, { lerp, duration, easing, onStart, onUpdate }) {
116
+ this.from = this.value = from;
117
+ this.to = to;
118
+ this.lerp = lerp;
119
+ this.duration = duration;
120
+ this.easing = easing;
121
+ this.currentTime = 0;
122
+ this.isRunning = true;
123
+ onStart?.();
124
+ this.onUpdate = onUpdate;
125
+ }
126
+ };
158
127
 
159
- // packages/core/src/emitter.ts
160
- var Emitter = class {
161
- events = {};
162
- /**
163
- * Emit an event with the given data
164
- * @param event Event name
165
- * @param args Data to pass to the event handlers
166
- */
167
- emit(event, ...args) {
168
- const callbacks = this.events[event] || [];
169
- for (let i = 0, length = callbacks.length; i < length; i++) {
170
- callbacks[i]?.(...args);
171
- }
172
- }
173
- /**
174
- * Add a callback to the event
175
- * @param event Event name
176
- * @param cb Callback function
177
- * @returns Unsubscribe function
178
- */
179
- on(event, cb) {
180
- if (this.events[event]) {
181
- this.events[event].push(cb);
182
- } else {
183
- this.events[event] = [cb];
184
- }
185
- return () => {
186
- this.events[event] = this.events[event]?.filter((i) => cb !== i);
187
- };
188
- }
189
- /**
190
- * Remove a callback from the event
191
- * @param event Event name
192
- * @param callback Callback function
193
- */
194
- off(event, callback) {
195
- this.events[event] = this.events[event]?.filter((i) => callback !== i);
196
- }
197
- /**
198
- * Remove all event listeners and clean up
199
- */
200
- destroy() {
201
- this.events = {};
202
- }
203
- };
128
+ //#endregion
129
+ //#region packages/core/src/debounce.ts
130
+ function debounce(callback, delay) {
131
+ let timer;
132
+ return function(...args) {
133
+ clearTimeout(timer);
134
+ timer = setTimeout(() => {
135
+ timer = void 0;
136
+ callback.apply(this, args);
137
+ }, delay);
138
+ };
139
+ }
204
140
 
205
- // packages/core/src/virtual-scroll.ts
206
- var LINE_HEIGHT = 100 / 6;
207
- var listenerOptions = { passive: false };
208
- function getDeltaMultiplier(deltaMode, size) {
209
- if (deltaMode === 1) return LINE_HEIGHT;
210
- if (deltaMode === 2) return size;
211
- return 1;
212
- }
213
- var VirtualScroll = class {
214
- constructor(element, options = { wheelMultiplier: 1, touchMultiplier: 1 }) {
215
- this.element = element;
216
- this.options = options;
217
- window.addEventListener("resize", this.onWindowResize);
218
- this.onWindowResize();
219
- this.element.addEventListener("wheel", this.onWheel, listenerOptions);
220
- this.element.addEventListener(
221
- "touchstart",
222
- this.onTouchStart,
223
- listenerOptions
224
- );
225
- this.element.addEventListener(
226
- "touchmove",
227
- this.onTouchMove,
228
- listenerOptions
229
- );
230
- this.element.addEventListener("touchend", this.onTouchEnd, listenerOptions);
231
- }
232
- touchStart = {
233
- x: 0,
234
- y: 0
235
- };
236
- lastDelta = {
237
- x: 0,
238
- y: 0
239
- };
240
- window = {
241
- width: 0,
242
- height: 0
243
- };
244
- emitter = new Emitter();
245
- /**
246
- * Add an event listener for the given event and callback
247
- *
248
- * @param event Event name
249
- * @param callback Callback function
250
- */
251
- on(event, callback) {
252
- return this.emitter.on(event, callback);
253
- }
254
- /** Remove all event listeners and clean up */
255
- destroy() {
256
- this.emitter.destroy();
257
- window.removeEventListener("resize", this.onWindowResize);
258
- this.element.removeEventListener("wheel", this.onWheel, listenerOptions);
259
- this.element.removeEventListener(
260
- "touchstart",
261
- this.onTouchStart,
262
- listenerOptions
263
- );
264
- this.element.removeEventListener(
265
- "touchmove",
266
- this.onTouchMove,
267
- listenerOptions
268
- );
269
- this.element.removeEventListener(
270
- "touchend",
271
- this.onTouchEnd,
272
- listenerOptions
273
- );
274
- }
275
- /**
276
- * Event handler for 'touchstart' event
277
- *
278
- * @param event Touch event
279
- */
280
- onTouchStart = (event) => {
281
- const { clientX, clientY } = event.targetTouches ? event.targetTouches[0] : event;
282
- this.touchStart.x = clientX;
283
- this.touchStart.y = clientY;
284
- this.lastDelta = {
285
- x: 0,
286
- y: 0
287
- };
288
- this.emitter.emit("scroll", {
289
- deltaX: 0,
290
- deltaY: 0,
291
- event
292
- });
293
- };
294
- /** Event handler for 'touchmove' event */
295
- onTouchMove = (event) => {
296
- const { clientX, clientY } = event.targetTouches ? event.targetTouches[0] : event;
297
- const deltaX = -(clientX - this.touchStart.x) * this.options.touchMultiplier;
298
- const deltaY = -(clientY - this.touchStart.y) * this.options.touchMultiplier;
299
- this.touchStart.x = clientX;
300
- this.touchStart.y = clientY;
301
- this.lastDelta = {
302
- x: deltaX,
303
- y: deltaY
304
- };
305
- this.emitter.emit("scroll", {
306
- deltaX,
307
- deltaY,
308
- event
309
- });
310
- };
311
- onTouchEnd = (event) => {
312
- this.emitter.emit("scroll", {
313
- deltaX: this.lastDelta.x,
314
- deltaY: this.lastDelta.y,
315
- event
316
- });
317
- };
318
- /** Event handler for 'wheel' event */
319
- onWheel = (event) => {
320
- let { deltaX, deltaY, deltaMode } = event;
321
- const multiplierX = getDeltaMultiplier(deltaMode, this.window.width);
322
- const multiplierY = getDeltaMultiplier(deltaMode, this.window.height);
323
- deltaX *= multiplierX;
324
- deltaY *= multiplierY;
325
- deltaX *= this.options.wheelMultiplier;
326
- deltaY *= this.options.wheelMultiplier;
327
- this.emitter.emit("scroll", { deltaX, deltaY, event });
328
- };
329
- onWindowResize = () => {
330
- this.window = {
331
- width: window.innerWidth,
332
- height: window.innerHeight
333
- };
334
- };
335
- };
141
+ //#endregion
142
+ //#region packages/core/src/dimensions.ts
143
+ /**
144
+ * Dimensions class to handle the size of the content and wrapper
145
+ *
146
+ * @example
147
+ * const dimensions = new Dimensions(wrapper, content)
148
+ * dimensions.on('resize', (e) => {
149
+ * console.log(e.width, e.height)
150
+ * })
151
+ */
152
+ var Dimensions = class {
153
+ width = 0;
154
+ height = 0;
155
+ scrollHeight = 0;
156
+ scrollWidth = 0;
157
+ debouncedResize;
158
+ wrapperResizeObserver;
159
+ contentResizeObserver;
160
+ constructor(wrapper, content, { autoResize = true, debounce: debounceValue = 250 } = {}) {
161
+ this.wrapper = wrapper;
162
+ this.content = content;
163
+ if (autoResize) {
164
+ this.debouncedResize = debounce(this.resize, debounceValue);
165
+ if (this.wrapper instanceof Window) window.addEventListener("resize", this.debouncedResize);
166
+ else {
167
+ this.wrapperResizeObserver = new ResizeObserver(this.debouncedResize);
168
+ this.wrapperResizeObserver.observe(this.wrapper);
169
+ }
170
+ this.contentResizeObserver = new ResizeObserver(this.debouncedResize);
171
+ this.contentResizeObserver.observe(this.content);
172
+ }
173
+ this.resize();
174
+ }
175
+ destroy() {
176
+ this.wrapperResizeObserver?.disconnect();
177
+ this.contentResizeObserver?.disconnect();
178
+ if (this.wrapper === window && this.debouncedResize) window.removeEventListener("resize", this.debouncedResize);
179
+ }
180
+ resize = () => {
181
+ this.onWrapperResize();
182
+ this.onContentResize();
183
+ };
184
+ onWrapperResize = () => {
185
+ if (this.wrapper instanceof Window) {
186
+ this.width = window.innerWidth;
187
+ this.height = window.innerHeight;
188
+ } else {
189
+ this.width = this.wrapper.clientWidth;
190
+ this.height = this.wrapper.clientHeight;
191
+ }
192
+ };
193
+ onContentResize = () => {
194
+ if (this.wrapper instanceof Window) {
195
+ this.scrollHeight = this.content.scrollHeight;
196
+ this.scrollWidth = this.content.scrollWidth;
197
+ } else {
198
+ this.scrollHeight = this.wrapper.scrollHeight;
199
+ this.scrollWidth = this.wrapper.scrollWidth;
200
+ }
201
+ };
202
+ get limit() {
203
+ return {
204
+ x: this.scrollWidth - this.width,
205
+ y: this.scrollHeight - this.height
206
+ };
207
+ }
208
+ };
336
209
 
337
- // packages/core/src/lenis.ts
338
- var defaultEasing = (t) => Math.min(1, 1.001 - 2 ** (-10 * t));
339
- var Lenis = class {
340
- _isScrolling = false;
341
- // true when scroll is animating
342
- _isStopped = false;
343
- // true if user should not be able to scroll - enable/disable programmatically
344
- _isLocked = false;
345
- // same as isStopped but enabled/disabled when scroll reaches target
346
- _preventNextNativeScrollEvent = false;
347
- _resetVelocityTimeout = null;
348
- _rafId = null;
349
- /**
350
- * Whether or not the user is touching the screen
351
- */
352
- isTouching;
353
- /**
354
- * The time in ms since the lenis instance was created
355
- */
356
- time = 0;
357
- /**
358
- * User data that will be forwarded through the scroll event
359
- *
360
- * @example
361
- * lenis.scrollTo(100, {
362
- * userData: {
363
- * foo: 'bar'
364
- * }
365
- * })
366
- */
367
- userData = {};
368
- /**
369
- * The last velocity of the scroll
370
- */
371
- lastVelocity = 0;
372
- /**
373
- * The current velocity of the scroll
374
- */
375
- velocity = 0;
376
- /**
377
- * The direction of the scroll
378
- */
379
- direction = 0;
380
- /**
381
- * The options passed to the lenis instance
382
- */
383
- options;
384
- /**
385
- * The target scroll value
386
- */
387
- targetScroll;
388
- /**
389
- * The animated scroll value
390
- */
391
- animatedScroll;
392
- // These are instanciated here as they don't need information from the options
393
- animate = new Animate();
394
- emitter = new Emitter();
395
- // These are instanciated in the constructor as they need information from the options
396
- dimensions;
397
- // This is not private because it's used in the Snap class
398
- virtualScroll;
399
- constructor({
400
- wrapper = window,
401
- content = document.documentElement,
402
- eventsTarget = wrapper,
403
- smoothWheel = true,
404
- syncTouch = false,
405
- syncTouchLerp = 0.075,
406
- touchInertiaExponent = 1.7,
407
- duration,
408
- // in seconds
409
- easing,
410
- lerp: lerp2 = 0.1,
411
- infinite = false,
412
- orientation = "vertical",
413
- // vertical, horizontal
414
- gestureOrientation = orientation === "horizontal" ? "both" : "vertical",
415
- // vertical, horizontal, both
416
- touchMultiplier = 1,
417
- wheelMultiplier = 1,
418
- autoResize = true,
419
- prevent,
420
- virtualScroll,
421
- overscroll = true,
422
- autoRaf = false,
423
- anchors = false,
424
- autoToggle = false,
425
- // https://caniuse.com/?search=transition-behavior
426
- allowNestedScroll = false,
427
- __experimental__naiveDimensions = false,
428
- naiveDimensions = __experimental__naiveDimensions,
429
- stopInertiaOnNavigate = false
430
- } = {}) {
431
- window.lenisVersion = version;
432
- if (!window.lenis) {
433
- window.lenis = {};
434
- }
435
- window.lenis.version = version;
436
- if (orientation === "horizontal") {
437
- window.lenis.horizontal = true;
438
- }
439
- if (syncTouch === true) {
440
- window.lenis.touch = true;
441
- }
442
- if (!wrapper || wrapper === document.documentElement) {
443
- wrapper = window;
444
- }
445
- if (typeof duration === "number" && typeof easing !== "function") {
446
- easing = defaultEasing;
447
- } else if (typeof easing === "function" && typeof duration !== "number") {
448
- duration = 1;
449
- }
450
- this.options = {
451
- wrapper,
452
- content,
453
- eventsTarget,
454
- smoothWheel,
455
- syncTouch,
456
- syncTouchLerp,
457
- touchInertiaExponent,
458
- duration,
459
- easing,
460
- lerp: lerp2,
461
- infinite,
462
- gestureOrientation,
463
- orientation,
464
- touchMultiplier,
465
- wheelMultiplier,
466
- autoResize,
467
- prevent,
468
- virtualScroll,
469
- overscroll,
470
- autoRaf,
471
- anchors,
472
- autoToggle,
473
- allowNestedScroll,
474
- naiveDimensions,
475
- stopInertiaOnNavigate
476
- };
477
- this.dimensions = new Dimensions(wrapper, content, { autoResize });
478
- this.updateClassName();
479
- this.targetScroll = this.animatedScroll = this.actualScroll;
480
- this.options.wrapper.addEventListener("scroll", this.onNativeScroll);
481
- this.options.wrapper.addEventListener("scrollend", this.onScrollEnd, {
482
- capture: true
483
- });
484
- if (this.options.anchors || this.options.stopInertiaOnNavigate) {
485
- this.options.wrapper.addEventListener(
486
- "click",
487
- this.onClick
488
- );
489
- }
490
- this.options.wrapper.addEventListener(
491
- "pointerdown",
492
- this.onPointerDown
493
- );
494
- this.virtualScroll = new VirtualScroll(eventsTarget, {
495
- touchMultiplier,
496
- wheelMultiplier
497
- });
498
- this.virtualScroll.on("scroll", this.onVirtualScroll);
499
- if (this.options.autoToggle) {
500
- this.checkOverflow();
501
- this.rootElement.addEventListener("transitionend", this.onTransitionEnd);
502
- }
503
- if (this.options.autoRaf) {
504
- this._rafId = requestAnimationFrame(this.raf);
505
- }
506
- }
507
- /**
508
- * Destroy the lenis instance, remove all event listeners and clean up the class name
509
- */
510
- destroy() {
511
- this.emitter.destroy();
512
- this.options.wrapper.removeEventListener("scroll", this.onNativeScroll);
513
- this.options.wrapper.removeEventListener("scrollend", this.onScrollEnd, {
514
- capture: true
515
- });
516
- this.options.wrapper.removeEventListener(
517
- "pointerdown",
518
- this.onPointerDown
519
- );
520
- if (this.options.anchors || this.options.stopInertiaOnNavigate) {
521
- this.options.wrapper.removeEventListener(
522
- "click",
523
- this.onClick
524
- );
525
- }
526
- this.virtualScroll.destroy();
527
- this.dimensions.destroy();
528
- this.cleanUpClassName();
529
- if (this._rafId) {
530
- cancelAnimationFrame(this._rafId);
531
- }
532
- }
533
- on(event, callback) {
534
- return this.emitter.on(event, callback);
535
- }
536
- off(event, callback) {
537
- return this.emitter.off(event, callback);
538
- }
539
- onScrollEnd = (e) => {
540
- if (!(e instanceof CustomEvent)) {
541
- if (this.isScrolling === "smooth" || this.isScrolling === false) {
542
- e.stopPropagation();
543
- }
544
- }
545
- };
546
- dispatchScrollendEvent = () => {
547
- this.options.wrapper.dispatchEvent(
548
- new CustomEvent("scrollend", {
549
- bubbles: this.options.wrapper === window,
550
- // cancelable: false,
551
- detail: {
552
- lenisScrollEnd: true
553
- }
554
- })
555
- );
556
- };
557
- get overflow() {
558
- const property = this.isHorizontal ? "overflow-x" : "overflow-y";
559
- return getComputedStyle(this.rootElement)[property];
560
- }
561
- checkOverflow() {
562
- if (["hidden", "clip"].includes(this.overflow)) {
563
- this.internalStop();
564
- } else {
565
- this.internalStart();
566
- }
567
- }
568
- onTransitionEnd = (event) => {
569
- if (event.propertyName.includes("overflow")) {
570
- this.checkOverflow();
571
- }
572
- };
573
- setScroll(scroll) {
574
- if (this.isHorizontal) {
575
- this.options.wrapper.scrollTo({ left: scroll, behavior: "instant" });
576
- } else {
577
- this.options.wrapper.scrollTo({ top: scroll, behavior: "instant" });
578
- }
579
- }
580
- onClick = (event) => {
581
- const path = event.composedPath();
582
- const linkElements = path.filter(
583
- (node) => node instanceof HTMLAnchorElement && node.href
584
- );
585
- const linkElementsUrls = linkElements.map(
586
- (element) => new URL(element.href)
587
- );
588
- const currentUrl = new URL(window.location.href);
589
- if (this.options.anchors) {
590
- const anchorElementUrl = linkElementsUrls.find(
591
- (targetUrl) => currentUrl.host === targetUrl.host && currentUrl.pathname === targetUrl.pathname && targetUrl.hash
592
- );
593
- if (anchorElementUrl) {
594
- const options = typeof this.options.anchors === "object" && this.options.anchors ? this.options.anchors : void 0;
595
- const target = `#${anchorElementUrl.hash.split("#")[1]}`;
596
- this.scrollTo(target, options);
597
- return;
598
- }
599
- }
600
- if (this.options.stopInertiaOnNavigate) {
601
- const hasPageLinkElementUrl = linkElementsUrls.some(
602
- (targetUrl) => currentUrl.host === targetUrl.host && currentUrl.pathname !== targetUrl.pathname
603
- );
604
- if (hasPageLinkElementUrl) {
605
- this.reset();
606
- return;
607
- }
608
- }
609
- };
610
- onPointerDown = (event) => {
611
- if (event.button === 1) {
612
- this.reset();
613
- }
614
- };
615
- onVirtualScroll = (data) => {
616
- if (typeof this.options.virtualScroll === "function" && this.options.virtualScroll(data) === false)
617
- return;
618
- const { deltaX, deltaY, event } = data;
619
- this.emitter.emit("virtual-scroll", { deltaX, deltaY, event });
620
- if (event.ctrlKey) return;
621
- if (event.lenisStopPropagation) return;
622
- const isTouch = event.type.includes("touch");
623
- const isWheel = event.type.includes("wheel");
624
- this.isTouching = event.type === "touchstart" || event.type === "touchmove";
625
- const isClickOrTap = deltaX === 0 && deltaY === 0;
626
- const isTapToStop = this.options.syncTouch && isTouch && event.type === "touchstart" && isClickOrTap && !this.isStopped && !this.isLocked;
627
- if (isTapToStop) {
628
- this.reset();
629
- return;
630
- }
631
- const isUnknownGesture = this.options.gestureOrientation === "vertical" && deltaY === 0 || this.options.gestureOrientation === "horizontal" && deltaX === 0;
632
- if (isClickOrTap || isUnknownGesture) {
633
- return;
634
- }
635
- let composedPath = event.composedPath();
636
- composedPath = composedPath.slice(0, composedPath.indexOf(this.rootElement));
637
- const prevent = this.options.prevent;
638
- const gestureOrientation = Math.abs(deltaX) >= Math.abs(deltaY) ? "horizontal" : "vertical";
639
- if (composedPath.find(
640
- (node) => node instanceof HTMLElement && (typeof prevent === "function" && prevent?.(node) || node.hasAttribute?.("data-lenis-prevent") || gestureOrientation === "vertical" && node.hasAttribute?.("data-lenis-prevent-vertical") || gestureOrientation === "horizontal" && node.hasAttribute?.("data-lenis-prevent-horizontal") || isTouch && node.hasAttribute?.("data-lenis-prevent-touch") || isWheel && node.hasAttribute?.("data-lenis-prevent-wheel") || this.options.allowNestedScroll && this.hasNestedScroll(node, {
641
- deltaX,
642
- deltaY
643
- }))
644
- ))
645
- return;
646
- if (this.isStopped || this.isLocked) {
647
- if (event.cancelable) {
648
- event.preventDefault();
649
- }
650
- return;
651
- }
652
- const isSmooth = this.options.syncTouch && isTouch || this.options.smoothWheel && isWheel;
653
- if (!isSmooth) {
654
- this.isScrolling = "native";
655
- this.animate.stop();
656
- event.lenisStopPropagation = true;
657
- return;
658
- }
659
- let delta = deltaY;
660
- if (this.options.gestureOrientation === "both") {
661
- delta = Math.abs(deltaY) > Math.abs(deltaX) ? deltaY : deltaX;
662
- } else if (this.options.gestureOrientation === "horizontal") {
663
- delta = deltaX;
664
- }
665
- if (!this.options.overscroll || this.options.infinite || this.options.wrapper !== window && this.limit > 0 && (this.animatedScroll > 0 && this.animatedScroll < this.limit || this.animatedScroll === 0 && deltaY > 0 || this.animatedScroll === this.limit && deltaY < 0)) {
666
- event.lenisStopPropagation = true;
667
- }
668
- if (event.cancelable) {
669
- event.preventDefault();
670
- }
671
- const isSyncTouch = isTouch && this.options.syncTouch;
672
- const isTouchEnd = isTouch && event.type === "touchend";
673
- const hasTouchInertia = isTouchEnd;
674
- if (hasTouchInertia) {
675
- delta = Math.sign(this.velocity) * Math.abs(this.velocity) ** this.options.touchInertiaExponent;
676
- }
677
- this.scrollTo(this.targetScroll + delta, {
678
- programmatic: false,
679
- ...isSyncTouch ? {
680
- lerp: hasTouchInertia ? this.options.syncTouchLerp : 1
681
- } : {
682
- lerp: this.options.lerp,
683
- duration: this.options.duration,
684
- easing: this.options.easing
685
- }
686
- });
687
- };
688
- /**
689
- * Force lenis to recalculate the dimensions
690
- */
691
- resize() {
692
- this.dimensions.resize();
693
- this.animatedScroll = this.targetScroll = this.actualScroll;
694
- this.emit();
695
- }
696
- emit() {
697
- this.emitter.emit("scroll", this);
698
- }
699
- onNativeScroll = () => {
700
- if (this._resetVelocityTimeout !== null) {
701
- clearTimeout(this._resetVelocityTimeout);
702
- this._resetVelocityTimeout = null;
703
- }
704
- if (this._preventNextNativeScrollEvent) {
705
- this._preventNextNativeScrollEvent = false;
706
- return;
707
- }
708
- if (this.isScrolling === false || this.isScrolling === "native") {
709
- const lastScroll = this.animatedScroll;
710
- this.animatedScroll = this.targetScroll = this.actualScroll;
711
- this.lastVelocity = this.velocity;
712
- this.velocity = this.animatedScroll - lastScroll;
713
- this.direction = Math.sign(
714
- this.animatedScroll - lastScroll
715
- );
716
- if (!this.isStopped) {
717
- this.isScrolling = "native";
718
- }
719
- this.emit();
720
- if (this.velocity !== 0) {
721
- this._resetVelocityTimeout = setTimeout(() => {
722
- this.lastVelocity = this.velocity;
723
- this.velocity = 0;
724
- this.isScrolling = false;
725
- this.emit();
726
- }, 400);
727
- }
728
- }
729
- };
730
- reset() {
731
- this.isLocked = false;
732
- this.isScrolling = false;
733
- this.animatedScroll = this.targetScroll = this.actualScroll;
734
- this.lastVelocity = this.velocity = 0;
735
- this.animate.stop();
736
- }
737
- /**
738
- * Start lenis scroll after it has been stopped
739
- */
740
- start() {
741
- if (!this.isStopped) return;
742
- if (this.options.autoToggle) {
743
- this.rootElement.style.removeProperty("overflow");
744
- return;
745
- }
746
- this.internalStart();
747
- }
748
- internalStart() {
749
- if (!this.isStopped) return;
750
- this.reset();
751
- this.isStopped = false;
752
- this.emit();
753
- }
754
- /**
755
- * Stop lenis scroll
756
- */
757
- stop() {
758
- if (this.isStopped) return;
759
- if (this.options.autoToggle) {
760
- this.rootElement.style.setProperty("overflow", "clip");
761
- return;
762
- }
763
- this.internalStop();
764
- }
765
- internalStop() {
766
- if (this.isStopped) return;
767
- this.reset();
768
- this.isStopped = true;
769
- this.emit();
770
- }
771
- /**
772
- * RequestAnimationFrame for lenis
773
- *
774
- * @param time The time in ms from an external clock like `requestAnimationFrame` or Tempus
775
- */
776
- raf = (time) => {
777
- const deltaTime = time - (this.time || time);
778
- this.time = time;
779
- this.animate.advance(deltaTime * 1e-3);
780
- if (this.options.autoRaf) {
781
- this._rafId = requestAnimationFrame(this.raf);
782
- }
783
- };
784
- /**
785
- * Scroll to a target value
786
- *
787
- * @param target The target value to scroll to
788
- * @param options The options for the scroll
789
- *
790
- * @example
791
- * lenis.scrollTo(100, {
792
- * offset: 100,
793
- * duration: 1,
794
- * easing: (t) => 1 - Math.cos((t * Math.PI) / 2),
795
- * lerp: 0.1,
796
- * onStart: () => {
797
- * console.log('onStart')
798
- * },
799
- * onComplete: () => {
800
- * console.log('onComplete')
801
- * },
802
- * })
803
- */
804
- scrollTo(_target, {
805
- offset = 0,
806
- immediate = false,
807
- lock = false,
808
- programmatic = true,
809
- // called from outside of the class
810
- lerp: lerp2 = programmatic ? this.options.lerp : void 0,
811
- duration = programmatic ? this.options.duration : void 0,
812
- easing = programmatic ? this.options.easing : void 0,
813
- onStart,
814
- onComplete,
815
- force = false,
816
- // scroll even if stopped
817
- userData
818
- } = {}) {
819
- if ((this.isStopped || this.isLocked) && !force) return;
820
- let target = _target;
821
- let adjustedOffset = offset;
822
- if (typeof target === "string" && ["top", "left", "start", "#"].includes(target)) {
823
- target = 0;
824
- } else if (typeof target === "string" && ["bottom", "right", "end"].includes(target)) {
825
- target = this.limit;
826
- } else {
827
- let node = null;
828
- if (typeof target === "string") {
829
- node = document.querySelector(target);
830
- if (!node) {
831
- if (target === "#top") {
832
- target = 0;
833
- } else {
834
- console.warn("Lenis: Target not found", target);
835
- }
836
- }
837
- } else if (target instanceof HTMLElement && target?.nodeType) {
838
- node = target;
839
- }
840
- if (node) {
841
- if (this.options.wrapper !== window) {
842
- const wrapperRect = this.rootElement.getBoundingClientRect();
843
- adjustedOffset -= this.isHorizontal ? wrapperRect.left : wrapperRect.top;
844
- }
845
- const rect = node.getBoundingClientRect();
846
- target = (this.isHorizontal ? rect.left : rect.top) + this.animatedScroll;
847
- }
848
- }
849
- if (typeof target !== "number") return;
850
- target += adjustedOffset;
851
- target = Math.round(target);
852
- if (this.options.infinite) {
853
- if (programmatic) {
854
- this.targetScroll = this.animatedScroll = this.scroll;
855
- const distance = target - this.animatedScroll;
856
- if (distance > this.limit / 2) {
857
- target -= this.limit;
858
- } else if (distance < -this.limit / 2) {
859
- target += this.limit;
860
- }
861
- }
862
- } else {
863
- target = clamp(0, target, this.limit);
864
- }
865
- if (target === this.targetScroll) {
866
- onStart?.(this);
867
- onComplete?.(this);
868
- return;
869
- }
870
- this.userData = userData ?? {};
871
- if (immediate) {
872
- this.animatedScroll = this.targetScroll = target;
873
- this.setScroll(this.scroll);
874
- this.reset();
875
- this.preventNextNativeScrollEvent();
876
- this.emit();
877
- onComplete?.(this);
878
- this.userData = {};
879
- requestAnimationFrame(() => {
880
- this.dispatchScrollendEvent();
881
- });
882
- return;
883
- }
884
- if (!programmatic) {
885
- this.targetScroll = target;
886
- }
887
- if (typeof duration === "number" && typeof easing !== "function") {
888
- easing = defaultEasing;
889
- } else if (typeof easing === "function" && typeof duration !== "number") {
890
- duration = 1;
891
- }
892
- this.animate.fromTo(this.animatedScroll, target, {
893
- duration,
894
- easing,
895
- lerp: lerp2,
896
- onStart: () => {
897
- if (lock) this.isLocked = true;
898
- this.isScrolling = "smooth";
899
- onStart?.(this);
900
- },
901
- onUpdate: (value, completed) => {
902
- this.isScrolling = "smooth";
903
- this.lastVelocity = this.velocity;
904
- this.velocity = value - this.animatedScroll;
905
- this.direction = Math.sign(this.velocity);
906
- this.animatedScroll = value;
907
- this.setScroll(this.scroll);
908
- if (programmatic) {
909
- this.targetScroll = value;
910
- }
911
- if (!completed) this.emit();
912
- if (completed) {
913
- this.reset();
914
- this.emit();
915
- onComplete?.(this);
916
- this.userData = {};
917
- requestAnimationFrame(() => {
918
- this.dispatchScrollendEvent();
919
- });
920
- this.preventNextNativeScrollEvent();
921
- }
922
- }
923
- });
924
- }
925
- preventNextNativeScrollEvent() {
926
- this._preventNextNativeScrollEvent = true;
927
- requestAnimationFrame(() => {
928
- this._preventNextNativeScrollEvent = false;
929
- });
930
- }
931
- hasNestedScroll(node, { deltaX, deltaY }) {
932
- const time = Date.now();
933
- if (!node._lenis) node._lenis = {};
934
- const cache = node._lenis;
935
- let hasOverflowX;
936
- let hasOverflowY;
937
- let isScrollableX;
938
- let isScrollableY;
939
- let hasOverscrollBehaviorX;
940
- let hasOverscrollBehaviorY;
941
- let scrollWidth;
942
- let scrollHeight;
943
- let clientWidth;
944
- let clientHeight;
945
- if (time - (cache.time ?? 0) > 2e3) {
946
- cache.time = Date.now();
947
- const computedStyle = window.getComputedStyle(node);
948
- cache.computedStyle = computedStyle;
949
- hasOverflowX = ["auto", "overlay", "scroll"].includes(
950
- computedStyle.overflowX
951
- );
952
- hasOverflowY = ["auto", "overlay", "scroll"].includes(
953
- computedStyle.overflowY
954
- );
955
- hasOverscrollBehaviorX = ["auto"].includes(
956
- computedStyle.overscrollBehaviorX
957
- );
958
- hasOverscrollBehaviorY = ["auto"].includes(
959
- computedStyle.overscrollBehaviorY
960
- );
961
- cache.hasOverflowX = hasOverflowX;
962
- cache.hasOverflowY = hasOverflowY;
963
- if (!(hasOverflowX || hasOverflowY)) return false;
964
- scrollWidth = node.scrollWidth;
965
- scrollHeight = node.scrollHeight;
966
- clientWidth = node.clientWidth;
967
- clientHeight = node.clientHeight;
968
- isScrollableX = scrollWidth > clientWidth;
969
- isScrollableY = scrollHeight > clientHeight;
970
- cache.isScrollableX = isScrollableX;
971
- cache.isScrollableY = isScrollableY;
972
- cache.scrollWidth = scrollWidth;
973
- cache.scrollHeight = scrollHeight;
974
- cache.clientWidth = clientWidth;
975
- cache.clientHeight = clientHeight;
976
- cache.hasOverscrollBehaviorX = hasOverscrollBehaviorX;
977
- cache.hasOverscrollBehaviorY = hasOverscrollBehaviorY;
978
- } else {
979
- isScrollableX = cache.isScrollableX;
980
- isScrollableY = cache.isScrollableY;
981
- hasOverflowX = cache.hasOverflowX;
982
- hasOverflowY = cache.hasOverflowY;
983
- scrollWidth = cache.scrollWidth;
984
- scrollHeight = cache.scrollHeight;
985
- clientWidth = cache.clientWidth;
986
- clientHeight = cache.clientHeight;
987
- hasOverscrollBehaviorX = cache.hasOverscrollBehaviorX;
988
- hasOverscrollBehaviorY = cache.hasOverscrollBehaviorY;
989
- }
990
- if (!(hasOverflowX && isScrollableX || hasOverflowY && isScrollableY)) {
991
- return false;
992
- }
993
- const orientation = Math.abs(deltaX) >= Math.abs(deltaY) ? "horizontal" : "vertical";
994
- let scroll;
995
- let maxScroll;
996
- let delta;
997
- let hasOverflow;
998
- let isScrollable;
999
- let hasOverscrollBehavior;
1000
- if (orientation === "horizontal") {
1001
- scroll = Math.round(node.scrollLeft);
1002
- maxScroll = scrollWidth - clientWidth;
1003
- delta = deltaX;
1004
- hasOverflow = hasOverflowX;
1005
- isScrollable = isScrollableX;
1006
- hasOverscrollBehavior = hasOverscrollBehaviorX;
1007
- } else if (orientation === "vertical") {
1008
- scroll = Math.round(node.scrollTop);
1009
- maxScroll = scrollHeight - clientHeight;
1010
- delta = deltaY;
1011
- hasOverflow = hasOverflowY;
1012
- isScrollable = isScrollableY;
1013
- hasOverscrollBehavior = hasOverscrollBehaviorY;
1014
- } else {
1015
- return false;
1016
- }
1017
- if (!hasOverscrollBehavior && (scroll >= maxScroll || scroll <= 0)) {
1018
- return true;
1019
- }
1020
- const willScroll = delta > 0 ? scroll < maxScroll : scroll > 0;
1021
- return willScroll && hasOverflow && isScrollable;
1022
- }
1023
- /**
1024
- * The root element on which lenis is instanced
1025
- */
1026
- get rootElement() {
1027
- return this.options.wrapper === window ? document.documentElement : this.options.wrapper;
1028
- }
1029
- /**
1030
- * The limit which is the maximum scroll value
1031
- */
1032
- get limit() {
1033
- if (this.options.naiveDimensions) {
1034
- if (this.isHorizontal) {
1035
- return this.rootElement.scrollWidth - this.rootElement.clientWidth;
1036
- }
1037
- return this.rootElement.scrollHeight - this.rootElement.clientHeight;
1038
- }
1039
- return this.dimensions.limit[this.isHorizontal ? "x" : "y"];
1040
- }
1041
- /**
1042
- * Whether or not the scroll is horizontal
1043
- */
1044
- get isHorizontal() {
1045
- return this.options.orientation === "horizontal";
1046
- }
1047
- /**
1048
- * The actual scroll value
1049
- */
1050
- get actualScroll() {
1051
- const wrapper = this.options.wrapper;
1052
- return this.isHorizontal ? wrapper.scrollX ?? wrapper.scrollLeft : wrapper.scrollY ?? wrapper.scrollTop;
1053
- }
1054
- /**
1055
- * The current scroll value
1056
- */
1057
- get scroll() {
1058
- return this.options.infinite ? modulo(this.animatedScroll, this.limit) : this.animatedScroll;
1059
- }
1060
- /**
1061
- * The progress of the scroll relative to the limit
1062
- */
1063
- get progress() {
1064
- return this.limit === 0 ? 1 : this.scroll / this.limit;
1065
- }
1066
- /**
1067
- * Current scroll state
1068
- */
1069
- get isScrolling() {
1070
- return this._isScrolling;
1071
- }
1072
- set isScrolling(value) {
1073
- if (this._isScrolling !== value) {
1074
- this._isScrolling = value;
1075
- this.updateClassName();
1076
- }
1077
- }
1078
- /**
1079
- * Check if lenis is stopped
1080
- */
1081
- get isStopped() {
1082
- return this._isStopped;
1083
- }
1084
- set isStopped(value) {
1085
- if (this._isStopped !== value) {
1086
- this._isStopped = value;
1087
- this.updateClassName();
1088
- }
1089
- }
1090
- /**
1091
- * Check if lenis is locked
1092
- */
1093
- get isLocked() {
1094
- return this._isLocked;
1095
- }
1096
- set isLocked(value) {
1097
- if (this._isLocked !== value) {
1098
- this._isLocked = value;
1099
- this.updateClassName();
1100
- }
1101
- }
1102
- /**
1103
- * Check if lenis is smooth scrolling
1104
- */
1105
- get isSmooth() {
1106
- return this.isScrolling === "smooth";
1107
- }
1108
- /**
1109
- * The class name applied to the wrapper element
1110
- */
1111
- get className() {
1112
- let className = "lenis";
1113
- if (this.options.autoToggle) className += " lenis-autoToggle";
1114
- if (this.isStopped) className += " lenis-stopped";
1115
- if (this.isLocked) className += " lenis-locked";
1116
- if (this.isScrolling) className += " lenis-scrolling";
1117
- if (this.isScrolling === "smooth") className += " lenis-smooth";
1118
- return className;
1119
- }
1120
- updateClassName() {
1121
- this.cleanUpClassName();
1122
- this.rootElement.className = `${this.rootElement.className} ${this.className}`.trim();
1123
- }
1124
- cleanUpClassName() {
1125
- this.rootElement.className = this.rootElement.className.replace(/lenis(-\w+)?/g, "").trim();
1126
- }
1127
- };
210
+ //#endregion
211
+ //#region packages/core/src/emitter.ts
212
+ /**
213
+ * Emitter class to handle events
214
+ * @example
215
+ * const emitter = new Emitter()
216
+ * emitter.on('event', (data) => {
217
+ * console.log(data)
218
+ * })
219
+ * emitter.emit('event', 'data')
220
+ */
221
+ var Emitter = class {
222
+ events = {};
223
+ /**
224
+ * Emit an event with the given data
225
+ * @param event Event name
226
+ * @param args Data to pass to the event handlers
227
+ */
228
+ emit(event, ...args) {
229
+ const callbacks = this.events[event] || [];
230
+ for (let i = 0, length = callbacks.length; i < length; i++) callbacks[i]?.(...args);
231
+ }
232
+ /**
233
+ * Add a callback to the event
234
+ * @param event Event name
235
+ * @param cb Callback function
236
+ * @returns Unsubscribe function
237
+ */
238
+ on(event, cb) {
239
+ if (this.events[event]) this.events[event].push(cb);
240
+ else this.events[event] = [cb];
241
+ return () => {
242
+ this.events[event] = this.events[event]?.filter((i) => cb !== i);
243
+ };
244
+ }
245
+ /**
246
+ * Remove a callback from the event
247
+ * @param event Event name
248
+ * @param callback Callback function
249
+ */
250
+ off(event, callback) {
251
+ this.events[event] = this.events[event]?.filter((i) => callback !== i);
252
+ }
253
+ /**
254
+ * Remove all event listeners and clean up
255
+ */
256
+ destroy() {
257
+ this.events = {};
258
+ }
259
+ };
1128
260
 
1129
- // packages/core/browser.ts
1130
- globalThis.Lenis = Lenis;
1131
- globalThis.Lenis.prototype = Lenis.prototype;
261
+ //#endregion
262
+ //#region packages/core/src/virtual-scroll.ts
263
+ const LINE_HEIGHT = 100 / 6;
264
+ const listenerOptions = { passive: false };
265
+ function getDeltaMultiplier(deltaMode, size) {
266
+ if (deltaMode === 1) return LINE_HEIGHT;
267
+ if (deltaMode === 2) return size;
268
+ return 1;
269
+ }
270
+ var VirtualScroll = class {
271
+ touchStart = {
272
+ x: 0,
273
+ y: 0
274
+ };
275
+ lastDelta = {
276
+ x: 0,
277
+ y: 0
278
+ };
279
+ window = {
280
+ width: 0,
281
+ height: 0
282
+ };
283
+ emitter = new Emitter();
284
+ constructor(element, options = {
285
+ wheelMultiplier: 1,
286
+ touchMultiplier: 1
287
+ }) {
288
+ this.element = element;
289
+ this.options = options;
290
+ window.addEventListener("resize", this.onWindowResize);
291
+ this.onWindowResize();
292
+ this.element.addEventListener("wheel", this.onWheel, listenerOptions);
293
+ this.element.addEventListener("touchstart", this.onTouchStart, listenerOptions);
294
+ this.element.addEventListener("touchmove", this.onTouchMove, listenerOptions);
295
+ this.element.addEventListener("touchend", this.onTouchEnd, listenerOptions);
296
+ }
297
+ /**
298
+ * Add an event listener for the given event and callback
299
+ *
300
+ * @param event Event name
301
+ * @param callback Callback function
302
+ */
303
+ on(event, callback) {
304
+ return this.emitter.on(event, callback);
305
+ }
306
+ /** Remove all event listeners and clean up */
307
+ destroy() {
308
+ this.emitter.destroy();
309
+ window.removeEventListener("resize", this.onWindowResize);
310
+ this.element.removeEventListener("wheel", this.onWheel, listenerOptions);
311
+ this.element.removeEventListener("touchstart", this.onTouchStart, listenerOptions);
312
+ this.element.removeEventListener("touchmove", this.onTouchMove, listenerOptions);
313
+ this.element.removeEventListener("touchend", this.onTouchEnd, listenerOptions);
314
+ }
315
+ /**
316
+ * Event handler for 'touchstart' event
317
+ *
318
+ * @param event Touch event
319
+ */
320
+ onTouchStart = (event) => {
321
+ const { clientX, clientY } = event.targetTouches ? event.targetTouches[0] : event;
322
+ this.touchStart.x = clientX;
323
+ this.touchStart.y = clientY;
324
+ this.lastDelta = {
325
+ x: 0,
326
+ y: 0
327
+ };
328
+ this.emitter.emit("scroll", {
329
+ deltaX: 0,
330
+ deltaY: 0,
331
+ event
332
+ });
333
+ };
334
+ /** Event handler for 'touchmove' event */
335
+ onTouchMove = (event) => {
336
+ const { clientX, clientY } = event.targetTouches ? event.targetTouches[0] : event;
337
+ const deltaX = -(clientX - this.touchStart.x) * this.options.touchMultiplier;
338
+ const deltaY = -(clientY - this.touchStart.y) * this.options.touchMultiplier;
339
+ this.touchStart.x = clientX;
340
+ this.touchStart.y = clientY;
341
+ this.lastDelta = {
342
+ x: deltaX,
343
+ y: deltaY
344
+ };
345
+ this.emitter.emit("scroll", {
346
+ deltaX,
347
+ deltaY,
348
+ event
349
+ });
350
+ };
351
+ onTouchEnd = (event) => {
352
+ this.emitter.emit("scroll", {
353
+ deltaX: this.lastDelta.x,
354
+ deltaY: this.lastDelta.y,
355
+ event
356
+ });
357
+ };
358
+ /** Event handler for 'wheel' event */
359
+ onWheel = (event) => {
360
+ let { deltaX, deltaY, deltaMode } = event;
361
+ const multiplierX = getDeltaMultiplier(deltaMode, this.window.width);
362
+ const multiplierY = getDeltaMultiplier(deltaMode, this.window.height);
363
+ deltaX *= multiplierX;
364
+ deltaY *= multiplierY;
365
+ deltaX *= this.options.wheelMultiplier;
366
+ deltaY *= this.options.wheelMultiplier;
367
+ this.emitter.emit("scroll", {
368
+ deltaX,
369
+ deltaY,
370
+ event
371
+ });
372
+ };
373
+ onWindowResize = () => {
374
+ this.window = {
375
+ width: window.innerWidth,
376
+ height: window.innerHeight
377
+ };
378
+ };
379
+ };
380
+
381
+ //#endregion
382
+ //#region packages/core/src/lenis.ts
383
+ const defaultEasing = (t) => Math.min(1, 1.001 - 2 ** (-10 * t));
384
+ var Lenis = class {
385
+ _isScrolling = false;
386
+ _isStopped = false;
387
+ _isLocked = false;
388
+ _preventNextNativeScrollEvent = false;
389
+ _resetVelocityTimeout = null;
390
+ _rafId = null;
391
+ /**
392
+ * Whether or not the user is touching the screen
393
+ */
394
+ isTouching;
395
+ /**
396
+ * The time in ms since the lenis instance was created
397
+ */
398
+ time = 0;
399
+ /**
400
+ * User data that will be forwarded through the scroll event
401
+ *
402
+ * @example
403
+ * lenis.scrollTo(100, {
404
+ * userData: {
405
+ * foo: 'bar'
406
+ * }
407
+ * })
408
+ */
409
+ userData = {};
410
+ /**
411
+ * The last velocity of the scroll
412
+ */
413
+ lastVelocity = 0;
414
+ /**
415
+ * The current velocity of the scroll
416
+ */
417
+ velocity = 0;
418
+ /**
419
+ * The direction of the scroll
420
+ */
421
+ direction = 0;
422
+ /**
423
+ * The options passed to the lenis instance
424
+ */
425
+ options;
426
+ /**
427
+ * The target scroll value
428
+ */
429
+ targetScroll;
430
+ /**
431
+ * The animated scroll value
432
+ */
433
+ animatedScroll;
434
+ animate = new Animate();
435
+ emitter = new Emitter();
436
+ dimensions;
437
+ virtualScroll;
438
+ constructor({ wrapper = window, content = document.documentElement, eventsTarget = wrapper, smoothWheel = true, syncTouch = false, syncTouchLerp = .075, touchInertiaExponent = 1.7, duration, easing, lerp = .1, infinite = false, orientation = "vertical", gestureOrientation = orientation === "horizontal" ? "both" : "vertical", touchMultiplier = 1, wheelMultiplier = 1, autoResize = true, prevent, virtualScroll, overscroll = true, autoRaf = false, anchors = false, autoToggle = false, allowNestedScroll = false, __experimental__naiveDimensions = false, naiveDimensions = __experimental__naiveDimensions, stopInertiaOnNavigate = false } = {}) {
439
+ window.lenisVersion = version;
440
+ if (!window.lenis) window.lenis = {};
441
+ window.lenis.version = version;
442
+ if (orientation === "horizontal") window.lenis.horizontal = true;
443
+ if (syncTouch === true) window.lenis.touch = true;
444
+ if (!wrapper || wrapper === document.documentElement) wrapper = window;
445
+ if (typeof duration === "number" && typeof easing !== "function") easing = defaultEasing;
446
+ else if (typeof easing === "function" && typeof duration !== "number") duration = 1;
447
+ this.options = {
448
+ wrapper,
449
+ content,
450
+ eventsTarget,
451
+ smoothWheel,
452
+ syncTouch,
453
+ syncTouchLerp,
454
+ touchInertiaExponent,
455
+ duration,
456
+ easing,
457
+ lerp,
458
+ infinite,
459
+ gestureOrientation,
460
+ orientation,
461
+ touchMultiplier,
462
+ wheelMultiplier,
463
+ autoResize,
464
+ prevent,
465
+ virtualScroll,
466
+ overscroll,
467
+ autoRaf,
468
+ anchors,
469
+ autoToggle,
470
+ allowNestedScroll,
471
+ naiveDimensions,
472
+ stopInertiaOnNavigate
473
+ };
474
+ this.dimensions = new Dimensions(wrapper, content, { autoResize });
475
+ this.updateClassName();
476
+ this.targetScroll = this.animatedScroll = this.actualScroll;
477
+ this.options.wrapper.addEventListener("scroll", this.onNativeScroll);
478
+ this.options.wrapper.addEventListener("scrollend", this.onScrollEnd, { capture: true });
479
+ if (this.options.anchors || this.options.stopInertiaOnNavigate) this.options.wrapper.addEventListener("click", this.onClick);
480
+ this.options.wrapper.addEventListener("pointerdown", this.onPointerDown);
481
+ this.virtualScroll = new VirtualScroll(eventsTarget, {
482
+ touchMultiplier,
483
+ wheelMultiplier
484
+ });
485
+ this.virtualScroll.on("scroll", this.onVirtualScroll);
486
+ if (this.options.autoToggle) {
487
+ this.checkOverflow();
488
+ this.rootElement.addEventListener("transitionend", this.onTransitionEnd);
489
+ }
490
+ if (this.options.autoRaf) this._rafId = requestAnimationFrame(this.raf);
491
+ }
492
+ /**
493
+ * Destroy the lenis instance, remove all event listeners and clean up the class name
494
+ */
495
+ destroy() {
496
+ this.emitter.destroy();
497
+ this.options.wrapper.removeEventListener("scroll", this.onNativeScroll);
498
+ this.options.wrapper.removeEventListener("scrollend", this.onScrollEnd, { capture: true });
499
+ this.options.wrapper.removeEventListener("pointerdown", this.onPointerDown);
500
+ if (this.options.anchors || this.options.stopInertiaOnNavigate) this.options.wrapper.removeEventListener("click", this.onClick);
501
+ this.virtualScroll.destroy();
502
+ this.dimensions.destroy();
503
+ this.cleanUpClassName();
504
+ if (this._rafId) cancelAnimationFrame(this._rafId);
505
+ }
506
+ on(event, callback) {
507
+ return this.emitter.on(event, callback);
508
+ }
509
+ off(event, callback) {
510
+ return this.emitter.off(event, callback);
511
+ }
512
+ onScrollEnd = (e) => {
513
+ if (!(e instanceof CustomEvent)) {
514
+ if (this.isScrolling === "smooth" || this.isScrolling === false) e.stopPropagation();
515
+ }
516
+ };
517
+ dispatchScrollendEvent = () => {
518
+ this.options.wrapper.dispatchEvent(new CustomEvent("scrollend", {
519
+ bubbles: this.options.wrapper === window,
520
+ detail: { lenisScrollEnd: true }
521
+ }));
522
+ };
523
+ get overflow() {
524
+ const property = this.isHorizontal ? "overflow-x" : "overflow-y";
525
+ return getComputedStyle(this.rootElement)[property];
526
+ }
527
+ checkOverflow() {
528
+ if (["hidden", "clip"].includes(this.overflow)) this.internalStop();
529
+ else this.internalStart();
530
+ }
531
+ onTransitionEnd = (event) => {
532
+ if (event.propertyName.includes("overflow")) this.checkOverflow();
533
+ };
534
+ setScroll(scroll) {
535
+ if (this.isHorizontal) this.options.wrapper.scrollTo({
536
+ left: scroll,
537
+ behavior: "instant"
538
+ });
539
+ else this.options.wrapper.scrollTo({
540
+ top: scroll,
541
+ behavior: "instant"
542
+ });
543
+ }
544
+ onClick = (event) => {
545
+ const linkElementsUrls = event.composedPath().filter((node) => node instanceof HTMLAnchorElement && node.href).map((element) => new URL(element.href));
546
+ const currentUrl = new URL(window.location.href);
547
+ if (this.options.anchors) {
548
+ const anchorElementUrl = linkElementsUrls.find((targetUrl) => currentUrl.host === targetUrl.host && currentUrl.pathname === targetUrl.pathname && targetUrl.hash);
549
+ if (anchorElementUrl) {
550
+ const options = typeof this.options.anchors === "object" && this.options.anchors ? this.options.anchors : void 0;
551
+ const target = `#${anchorElementUrl.hash.split("#")[1]}`;
552
+ this.scrollTo(target, options);
553
+ return;
554
+ }
555
+ }
556
+ if (this.options.stopInertiaOnNavigate) {
557
+ if (linkElementsUrls.some((targetUrl) => currentUrl.host === targetUrl.host && currentUrl.pathname !== targetUrl.pathname)) {
558
+ this.reset();
559
+ return;
560
+ }
561
+ }
562
+ };
563
+ onPointerDown = (event) => {
564
+ if (event.button === 1) this.reset();
565
+ };
566
+ onVirtualScroll = (data) => {
567
+ if (typeof this.options.virtualScroll === "function" && this.options.virtualScroll(data) === false) return;
568
+ const { deltaX, deltaY, event } = data;
569
+ this.emitter.emit("virtual-scroll", {
570
+ deltaX,
571
+ deltaY,
572
+ event
573
+ });
574
+ if (event.ctrlKey) return;
575
+ if (event.lenisStopPropagation) return;
576
+ const isTouch = event.type.includes("touch");
577
+ const isWheel = event.type.includes("wheel");
578
+ this.isTouching = event.type === "touchstart" || event.type === "touchmove";
579
+ const isClickOrTap = deltaX === 0 && deltaY === 0;
580
+ if (this.options.syncTouch && isTouch && event.type === "touchstart" && isClickOrTap && !this.isStopped && !this.isLocked) {
581
+ this.reset();
582
+ return;
583
+ }
584
+ const isUnknownGesture = this.options.gestureOrientation === "vertical" && deltaY === 0 || this.options.gestureOrientation === "horizontal" && deltaX === 0;
585
+ if (isClickOrTap || isUnknownGesture) return;
586
+ let composedPath = event.composedPath();
587
+ composedPath = composedPath.slice(0, composedPath.indexOf(this.rootElement));
588
+ const prevent = this.options.prevent;
589
+ const gestureOrientation = Math.abs(deltaX) >= Math.abs(deltaY) ? "horizontal" : "vertical";
590
+ if (composedPath.find((node) => node instanceof HTMLElement && (typeof prevent === "function" && prevent?.(node) || node.hasAttribute?.("data-lenis-prevent") || gestureOrientation === "vertical" && node.hasAttribute?.("data-lenis-prevent-vertical") || gestureOrientation === "horizontal" && node.hasAttribute?.("data-lenis-prevent-horizontal") || isTouch && node.hasAttribute?.("data-lenis-prevent-touch") || isWheel && node.hasAttribute?.("data-lenis-prevent-wheel") || this.options.allowNestedScroll && this.hasNestedScroll(node, {
591
+ deltaX,
592
+ deltaY
593
+ })))) return;
594
+ if (this.isStopped || this.isLocked) {
595
+ if (event.cancelable) event.preventDefault();
596
+ return;
597
+ }
598
+ if (!(this.options.syncTouch && isTouch || this.options.smoothWheel && isWheel)) {
599
+ this.isScrolling = "native";
600
+ this.animate.stop();
601
+ event.lenisStopPropagation = true;
602
+ return;
603
+ }
604
+ let delta = deltaY;
605
+ if (this.options.gestureOrientation === "both") delta = Math.abs(deltaY) > Math.abs(deltaX) ? deltaY : deltaX;
606
+ else if (this.options.gestureOrientation === "horizontal") delta = deltaX;
607
+ if (!this.options.overscroll || this.options.infinite || this.options.wrapper !== window && this.limit > 0 && (this.animatedScroll > 0 && this.animatedScroll < this.limit || this.animatedScroll === 0 && deltaY > 0 || this.animatedScroll === this.limit && deltaY < 0)) event.lenisStopPropagation = true;
608
+ if (event.cancelable) event.preventDefault();
609
+ const isSyncTouch = isTouch && this.options.syncTouch;
610
+ const hasTouchInertia = isTouch && event.type === "touchend";
611
+ if (hasTouchInertia) delta = Math.sign(this.velocity) * Math.abs(this.velocity) ** this.options.touchInertiaExponent;
612
+ this.scrollTo(this.targetScroll + delta, {
613
+ programmatic: false,
614
+ ...isSyncTouch ? { lerp: hasTouchInertia ? this.options.syncTouchLerp : 1 } : {
615
+ lerp: this.options.lerp,
616
+ duration: this.options.duration,
617
+ easing: this.options.easing
618
+ }
619
+ });
620
+ };
621
+ /**
622
+ * Force lenis to recalculate the dimensions
623
+ */
624
+ resize() {
625
+ this.dimensions.resize();
626
+ this.animatedScroll = this.targetScroll = this.actualScroll;
627
+ this.emit();
628
+ }
629
+ emit() {
630
+ this.emitter.emit("scroll", this);
631
+ }
632
+ onNativeScroll = () => {
633
+ if (this._resetVelocityTimeout !== null) {
634
+ clearTimeout(this._resetVelocityTimeout);
635
+ this._resetVelocityTimeout = null;
636
+ }
637
+ if (this._preventNextNativeScrollEvent) {
638
+ this._preventNextNativeScrollEvent = false;
639
+ return;
640
+ }
641
+ if (this.isScrolling === false || this.isScrolling === "native") {
642
+ const lastScroll = this.animatedScroll;
643
+ this.animatedScroll = this.targetScroll = this.actualScroll;
644
+ this.lastVelocity = this.velocity;
645
+ this.velocity = this.animatedScroll - lastScroll;
646
+ this.direction = Math.sign(this.animatedScroll - lastScroll);
647
+ if (!this.isStopped) this.isScrolling = "native";
648
+ this.emit();
649
+ if (this.velocity !== 0) this._resetVelocityTimeout = setTimeout(() => {
650
+ this.lastVelocity = this.velocity;
651
+ this.velocity = 0;
652
+ this.isScrolling = false;
653
+ this.emit();
654
+ }, 400);
655
+ }
656
+ };
657
+ reset() {
658
+ this.isLocked = false;
659
+ this.isScrolling = false;
660
+ this.animatedScroll = this.targetScroll = this.actualScroll;
661
+ this.lastVelocity = this.velocity = 0;
662
+ this.animate.stop();
663
+ }
664
+ /**
665
+ * Start lenis scroll after it has been stopped
666
+ */
667
+ start() {
668
+ if (!this.isStopped) return;
669
+ if (this.options.autoToggle) {
670
+ this.rootElement.style.removeProperty("overflow");
671
+ return;
672
+ }
673
+ this.internalStart();
674
+ }
675
+ internalStart() {
676
+ if (!this.isStopped) return;
677
+ this.reset();
678
+ this.isStopped = false;
679
+ this.emit();
680
+ }
681
+ /**
682
+ * Stop lenis scroll
683
+ */
684
+ stop() {
685
+ if (this.isStopped) return;
686
+ if (this.options.autoToggle) {
687
+ this.rootElement.style.setProperty("overflow", "clip");
688
+ return;
689
+ }
690
+ this.internalStop();
691
+ }
692
+ internalStop() {
693
+ if (this.isStopped) return;
694
+ this.reset();
695
+ this.isStopped = true;
696
+ this.emit();
697
+ }
698
+ /**
699
+ * RequestAnimationFrame for lenis
700
+ *
701
+ * @param time The time in ms from an external clock like `requestAnimationFrame` or Tempus
702
+ */
703
+ raf = (time) => {
704
+ const deltaTime = time - (this.time || time);
705
+ this.time = time;
706
+ this.animate.advance(deltaTime * .001);
707
+ if (this.options.autoRaf) this._rafId = requestAnimationFrame(this.raf);
708
+ };
709
+ /**
710
+ * Scroll to a target value
711
+ *
712
+ * @param target The target value to scroll to
713
+ * @param options The options for the scroll
714
+ *
715
+ * @example
716
+ * lenis.scrollTo(100, {
717
+ * offset: 100,
718
+ * duration: 1,
719
+ * easing: (t) => 1 - Math.cos((t * Math.PI) / 2),
720
+ * lerp: 0.1,
721
+ * onStart: () => {
722
+ * console.log('onStart')
723
+ * },
724
+ * onComplete: () => {
725
+ * console.log('onComplete')
726
+ * },
727
+ * })
728
+ */
729
+ scrollTo(_target, { offset = 0, immediate = false, lock = false, programmatic = true, lerp = programmatic ? this.options.lerp : void 0, duration = programmatic ? this.options.duration : void 0, easing = programmatic ? this.options.easing : void 0, onStart, onComplete, force = false, userData } = {}) {
730
+ if ((this.isStopped || this.isLocked) && !force) return;
731
+ let target = _target;
732
+ let adjustedOffset = offset;
733
+ if (typeof target === "string" && [
734
+ "top",
735
+ "left",
736
+ "start",
737
+ "#"
738
+ ].includes(target)) target = 0;
739
+ else if (typeof target === "string" && [
740
+ "bottom",
741
+ "right",
742
+ "end"
743
+ ].includes(target)) target = this.limit;
744
+ else {
745
+ let node = null;
746
+ if (typeof target === "string") {
747
+ node = document.querySelector(target);
748
+ if (!node) if (target === "#top") target = 0;
749
+ else console.warn("Lenis: Target not found", target);
750
+ } else if (target instanceof HTMLElement && target?.nodeType) node = target;
751
+ if (node) {
752
+ if (this.options.wrapper !== window) {
753
+ const wrapperRect = this.rootElement.getBoundingClientRect();
754
+ adjustedOffset -= this.isHorizontal ? wrapperRect.left : wrapperRect.top;
755
+ }
756
+ const rect = node.getBoundingClientRect();
757
+ const targetStyle = getComputedStyle(node);
758
+ const scrollMargin = this.isHorizontal ? Number.parseFloat(targetStyle.scrollMarginLeft) : Number.parseFloat(targetStyle.scrollMarginTop);
759
+ const containerStyle = getComputedStyle(this.rootElement);
760
+ const scrollPadding = this.isHorizontal ? Number.parseFloat(containerStyle.scrollPaddingLeft) : Number.parseFloat(containerStyle.scrollPaddingTop);
761
+ target = (this.isHorizontal ? rect.left : rect.top) + this.animatedScroll - (Number.isNaN(scrollMargin) ? 0 : scrollMargin) - (Number.isNaN(scrollPadding) ? 0 : scrollPadding);
762
+ }
763
+ }
764
+ if (typeof target !== "number") return;
765
+ target += adjustedOffset;
766
+ target = Math.round(target);
767
+ if (this.options.infinite) {
768
+ if (programmatic) {
769
+ this.targetScroll = this.animatedScroll = this.scroll;
770
+ const distance = target - this.animatedScroll;
771
+ if (distance > this.limit / 2) target -= this.limit;
772
+ else if (distance < -this.limit / 2) target += this.limit;
773
+ }
774
+ } else target = clamp(0, target, this.limit);
775
+ if (target === this.targetScroll) {
776
+ onStart?.(this);
777
+ onComplete?.(this);
778
+ return;
779
+ }
780
+ this.userData = userData ?? {};
781
+ if (immediate) {
782
+ this.animatedScroll = this.targetScroll = target;
783
+ this.setScroll(this.scroll);
784
+ this.reset();
785
+ this.preventNextNativeScrollEvent();
786
+ this.emit();
787
+ onComplete?.(this);
788
+ this.userData = {};
789
+ requestAnimationFrame(() => {
790
+ this.dispatchScrollendEvent();
791
+ });
792
+ return;
793
+ }
794
+ if (!programmatic) this.targetScroll = target;
795
+ if (typeof duration === "number" && typeof easing !== "function") easing = defaultEasing;
796
+ else if (typeof easing === "function" && typeof duration !== "number") duration = 1;
797
+ this.animate.fromTo(this.animatedScroll, target, {
798
+ duration,
799
+ easing,
800
+ lerp,
801
+ onStart: () => {
802
+ if (lock) this.isLocked = true;
803
+ this.isScrolling = "smooth";
804
+ onStart?.(this);
805
+ },
806
+ onUpdate: (value, completed) => {
807
+ this.isScrolling = "smooth";
808
+ this.lastVelocity = this.velocity;
809
+ this.velocity = value - this.animatedScroll;
810
+ this.direction = Math.sign(this.velocity);
811
+ this.animatedScroll = value;
812
+ this.setScroll(this.scroll);
813
+ if (programmatic) this.targetScroll = value;
814
+ if (!completed) this.emit();
815
+ if (completed) {
816
+ this.reset();
817
+ this.emit();
818
+ onComplete?.(this);
819
+ this.userData = {};
820
+ requestAnimationFrame(() => {
821
+ this.dispatchScrollendEvent();
822
+ });
823
+ this.preventNextNativeScrollEvent();
824
+ }
825
+ }
826
+ });
827
+ }
828
+ preventNextNativeScrollEvent() {
829
+ this._preventNextNativeScrollEvent = true;
830
+ requestAnimationFrame(() => {
831
+ this._preventNextNativeScrollEvent = false;
832
+ });
833
+ }
834
+ hasNestedScroll(node, { deltaX, deltaY }) {
835
+ const time = Date.now();
836
+ if (!node._lenis) node._lenis = {};
837
+ const cache = node._lenis;
838
+ let hasOverflowX;
839
+ let hasOverflowY;
840
+ let isScrollableX;
841
+ let isScrollableY;
842
+ let hasOverscrollBehaviorX;
843
+ let hasOverscrollBehaviorY;
844
+ let scrollWidth;
845
+ let scrollHeight;
846
+ let clientWidth;
847
+ let clientHeight;
848
+ if (time - (cache.time ?? 0) > 2e3) {
849
+ cache.time = Date.now();
850
+ const computedStyle = window.getComputedStyle(node);
851
+ cache.computedStyle = computedStyle;
852
+ hasOverflowX = [
853
+ "auto",
854
+ "overlay",
855
+ "scroll"
856
+ ].includes(computedStyle.overflowX);
857
+ hasOverflowY = [
858
+ "auto",
859
+ "overlay",
860
+ "scroll"
861
+ ].includes(computedStyle.overflowY);
862
+ hasOverscrollBehaviorX = ["auto"].includes(computedStyle.overscrollBehaviorX);
863
+ hasOverscrollBehaviorY = ["auto"].includes(computedStyle.overscrollBehaviorY);
864
+ cache.hasOverflowX = hasOverflowX;
865
+ cache.hasOverflowY = hasOverflowY;
866
+ if (!(hasOverflowX || hasOverflowY)) return false;
867
+ scrollWidth = node.scrollWidth;
868
+ scrollHeight = node.scrollHeight;
869
+ clientWidth = node.clientWidth;
870
+ clientHeight = node.clientHeight;
871
+ isScrollableX = scrollWidth > clientWidth;
872
+ isScrollableY = scrollHeight > clientHeight;
873
+ cache.isScrollableX = isScrollableX;
874
+ cache.isScrollableY = isScrollableY;
875
+ cache.scrollWidth = scrollWidth;
876
+ cache.scrollHeight = scrollHeight;
877
+ cache.clientWidth = clientWidth;
878
+ cache.clientHeight = clientHeight;
879
+ cache.hasOverscrollBehaviorX = hasOverscrollBehaviorX;
880
+ cache.hasOverscrollBehaviorY = hasOverscrollBehaviorY;
881
+ } else {
882
+ isScrollableX = cache.isScrollableX;
883
+ isScrollableY = cache.isScrollableY;
884
+ hasOverflowX = cache.hasOverflowX;
885
+ hasOverflowY = cache.hasOverflowY;
886
+ scrollWidth = cache.scrollWidth;
887
+ scrollHeight = cache.scrollHeight;
888
+ clientWidth = cache.clientWidth;
889
+ clientHeight = cache.clientHeight;
890
+ hasOverscrollBehaviorX = cache.hasOverscrollBehaviorX;
891
+ hasOverscrollBehaviorY = cache.hasOverscrollBehaviorY;
892
+ }
893
+ if (!(hasOverflowX && isScrollableX || hasOverflowY && isScrollableY)) return false;
894
+ const orientation = Math.abs(deltaX) >= Math.abs(deltaY) ? "horizontal" : "vertical";
895
+ let scroll;
896
+ let maxScroll;
897
+ let delta;
898
+ let hasOverflow;
899
+ let isScrollable;
900
+ let hasOverscrollBehavior;
901
+ if (orientation === "horizontal") {
902
+ scroll = Math.round(node.scrollLeft);
903
+ maxScroll = scrollWidth - clientWidth;
904
+ delta = deltaX;
905
+ hasOverflow = hasOverflowX;
906
+ isScrollable = isScrollableX;
907
+ hasOverscrollBehavior = hasOverscrollBehaviorX;
908
+ } else if (orientation === "vertical") {
909
+ scroll = Math.round(node.scrollTop);
910
+ maxScroll = scrollHeight - clientHeight;
911
+ delta = deltaY;
912
+ hasOverflow = hasOverflowY;
913
+ isScrollable = isScrollableY;
914
+ hasOverscrollBehavior = hasOverscrollBehaviorY;
915
+ } else return false;
916
+ if (!hasOverscrollBehavior && (scroll >= maxScroll || scroll <= 0)) return true;
917
+ return (delta > 0 ? scroll < maxScroll : scroll > 0) && hasOverflow && isScrollable;
918
+ }
919
+ /**
920
+ * The root element on which lenis is instanced
921
+ */
922
+ get rootElement() {
923
+ return this.options.wrapper === window ? document.documentElement : this.options.wrapper;
924
+ }
925
+ /**
926
+ * The limit which is the maximum scroll value
927
+ */
928
+ get limit() {
929
+ if (this.options.naiveDimensions) {
930
+ if (this.isHorizontal) return this.rootElement.scrollWidth - this.rootElement.clientWidth;
931
+ return this.rootElement.scrollHeight - this.rootElement.clientHeight;
932
+ }
933
+ return this.dimensions.limit[this.isHorizontal ? "x" : "y"];
934
+ }
935
+ /**
936
+ * Whether or not the scroll is horizontal
937
+ */
938
+ get isHorizontal() {
939
+ return this.options.orientation === "horizontal";
940
+ }
941
+ /**
942
+ * The actual scroll value
943
+ */
944
+ get actualScroll() {
945
+ const wrapper = this.options.wrapper;
946
+ return this.isHorizontal ? wrapper.scrollX ?? wrapper.scrollLeft : wrapper.scrollY ?? wrapper.scrollTop;
947
+ }
948
+ /**
949
+ * The current scroll value
950
+ */
951
+ get scroll() {
952
+ return this.options.infinite ? modulo(this.animatedScroll, this.limit) : this.animatedScroll;
953
+ }
954
+ /**
955
+ * The progress of the scroll relative to the limit
956
+ */
957
+ get progress() {
958
+ return this.limit === 0 ? 1 : this.scroll / this.limit;
959
+ }
960
+ /**
961
+ * Current scroll state
962
+ */
963
+ get isScrolling() {
964
+ return this._isScrolling;
965
+ }
966
+ set isScrolling(value) {
967
+ if (this._isScrolling !== value) {
968
+ this._isScrolling = value;
969
+ this.updateClassName();
970
+ }
971
+ }
972
+ /**
973
+ * Check if lenis is stopped
974
+ */
975
+ get isStopped() {
976
+ return this._isStopped;
977
+ }
978
+ set isStopped(value) {
979
+ if (this._isStopped !== value) {
980
+ this._isStopped = value;
981
+ this.updateClassName();
982
+ }
983
+ }
984
+ /**
985
+ * Check if lenis is locked
986
+ */
987
+ get isLocked() {
988
+ return this._isLocked;
989
+ }
990
+ set isLocked(value) {
991
+ if (this._isLocked !== value) {
992
+ this._isLocked = value;
993
+ this.updateClassName();
994
+ }
995
+ }
996
+ /**
997
+ * Check if lenis is smooth scrolling
998
+ */
999
+ get isSmooth() {
1000
+ return this.isScrolling === "smooth";
1001
+ }
1002
+ /**
1003
+ * The class name applied to the wrapper element
1004
+ */
1005
+ get className() {
1006
+ let className = "lenis";
1007
+ if (this.options.autoToggle) className += " lenis-autoToggle";
1008
+ if (this.isStopped) className += " lenis-stopped";
1009
+ if (this.isLocked) className += " lenis-locked";
1010
+ if (this.isScrolling) className += " lenis-scrolling";
1011
+ if (this.isScrolling === "smooth") className += " lenis-smooth";
1012
+ return className;
1013
+ }
1014
+ updateClassName() {
1015
+ this.cleanUpClassName();
1016
+ this.rootElement.className = `${this.rootElement.className} ${this.className}`.trim();
1017
+ }
1018
+ cleanUpClassName() {
1019
+ this.rootElement.className = this.rootElement.className.replace(/lenis(-\w+)?/g, "").trim();
1020
+ }
1021
+ };
1022
+
1023
+ //#endregion
1024
+ //#region packages/core/browser.ts
1025
+ globalThis.Lenis = Lenis;
1026
+ globalThis.Lenis.prototype = Lenis.prototype;
1027
+
1028
+ //#endregion
1029
+ })();
1132
1030
  //# sourceMappingURL=lenis.js.map