lenis 1.1.11 → 1.1.13

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,964 +1,923 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
- typeof define === 'function' && define.amd ? define(factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Lenis = factory());
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
+ typeof define === 'function' && define.amd ? define(factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Lenis = factory());
5
5
  })(this, (function () { 'use strict';
6
6
 
7
- /******************************************************************************
8
- Copyright (c) Microsoft Corporation.
9
-
10
- Permission to use, copy, modify, and/or distribute this software for any
11
- purpose with or without fee is hereby granted.
12
-
13
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
14
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
16
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
18
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19
- PERFORMANCE OF THIS SOFTWARE.
20
- ***************************************************************************** */
21
- /* global Reflect, Promise, SuppressedError, Symbol */
22
-
23
-
24
- function __classPrivateFieldGet(receiver, state, kind, f) {
25
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
26
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
27
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
28
- }
29
-
30
- function __classPrivateFieldSet(receiver, state, value, kind, f) {
31
- if (kind === "m") throw new TypeError("Private method is not writable");
32
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
33
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
34
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
35
- }
36
-
37
- typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
38
- var e = new Error(message);
39
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
40
- };
7
+ var version = "1.1.13";
41
8
 
42
- var version = "1.1.11";
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
+ }
43
55
 
44
- /**
45
- * Clamp a value between a minimum and maximum value
46
- *
47
- * @param min Minimum value
48
- * @param input Value to clamp
49
- * @param max Maximum value
50
- * @returns Clamped value
51
- */
52
- function clamp(min, input, max) {
53
- return Math.max(min, Math.min(input, max));
54
- }
55
- /**
56
- * Linearly interpolate between two values using an amount (0 <= t <= 1)
57
- *
58
- * @param x First value
59
- * @param y Second value
60
- * @param t Amount to interpolate (0 <= t <= 1)
61
- * @returns Interpolated value
62
- */
63
- function lerp(x, y, t) {
64
- return (1 - t) * x + t * y;
65
- }
66
- /**
67
- * Damp a value over time using a damping factor
68
- * {@link http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/}
69
- *
70
- * @param x Initial value
71
- * @param y Target value
72
- * @param lambda Damping factor
73
- * @param dt Time elapsed since the last update
74
- * @returns Damped value
75
- */
76
- function damp(x, y, lambda, deltaTime) {
77
- return lerp(x, y, 1 - Math.exp(-lambda * deltaTime));
78
- }
79
- /**
80
- * Calculate the modulo of the dividend and divisor while keeping the result within the same sign as the divisor
81
- * {@link https://anguscroll.com/just/just-modulo}
82
- *
83
- * @param n Dividend
84
- * @param d Divisor
85
- * @returns Modulo
86
- */
87
- function modulo(n, d) {
88
- return ((n % d) + d) % d;
89
- }
56
+ /**
57
+ * Animate class to handle value animations with lerping or easing
58
+ *
59
+ * @example
60
+ * const animate = new Animate()
61
+ * animate.fromTo(0, 100, { duration: 1, easing: (t) => t })
62
+ * animate.advance(0.5) // 50
63
+ */
64
+ class Animate {
65
+ constructor() {
66
+ this.isRunning = false;
67
+ this.value = 0;
68
+ this.from = 0;
69
+ this.to = 0;
70
+ this.currentTime = 0;
71
+ }
72
+ /**
73
+ * Advance the animation by the given delta time
74
+ *
75
+ * @param deltaTime - The time in seconds to advance the animation
76
+ */
77
+ advance(deltaTime) {
78
+ var _a;
79
+ if (!this.isRunning)
80
+ return;
81
+ let completed = false;
82
+ if (this.duration && this.easing) {
83
+ this.currentTime += deltaTime;
84
+ const linearProgress = clamp(0, this.currentTime / this.duration, 1);
85
+ completed = linearProgress >= 1;
86
+ const easedProgress = completed ? 1 : this.easing(linearProgress);
87
+ this.value = this.from + (this.to - this.from) * easedProgress;
88
+ }
89
+ else if (this.lerp) {
90
+ this.value = damp(this.value, this.to, this.lerp * 60, deltaTime);
91
+ if (Math.round(this.value) === this.to) {
92
+ this.value = this.to;
93
+ completed = true;
94
+ }
95
+ }
96
+ else {
97
+ // If no easing or lerp, just jump to the end value
98
+ this.value = this.to;
99
+ completed = true;
100
+ }
101
+ if (completed) {
102
+ this.stop();
103
+ }
104
+ // Call the onUpdate callback with the current value and completed status
105
+ (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.call(this, this.value, completed);
106
+ }
107
+ /** Stop the animation */
108
+ stop() {
109
+ this.isRunning = false;
110
+ }
111
+ /**
112
+ * Set up the animation from a starting value to an ending value
113
+ * with optional parameters for lerping, duration, easing, and onUpdate callback
114
+ *
115
+ * @param from - The starting value
116
+ * @param to - The ending value
117
+ * @param options - Options for the animation
118
+ */
119
+ fromTo(from, to, { lerp, duration, easing, onStart, onUpdate }) {
120
+ this.from = this.value = from;
121
+ this.to = to;
122
+ this.lerp = lerp;
123
+ this.duration = duration;
124
+ this.easing = easing;
125
+ this.currentTime = 0;
126
+ this.isRunning = true;
127
+ onStart === null || onStart === void 0 ? void 0 : onStart();
128
+ this.onUpdate = onUpdate;
129
+ }
130
+ }
90
131
 
91
- /**
92
- * Animate class to handle value animations with lerping or easing
93
- *
94
- * @example
95
- * const animate = new Animate()
96
- * animate.fromTo(0, 100, { duration: 1, easing: (t) => t })
97
- * animate.advance(0.5) // 50
98
- */
99
- class Animate {
100
- constructor() {
101
- this.isRunning = false;
102
- this.value = 0;
103
- this.from = 0;
104
- this.to = 0;
105
- this.currentTime = 0;
106
- }
107
- /**
108
- * Advance the animation by the given delta time
109
- *
110
- * @param deltaTime - The time in seconds to advance the animation
111
- */
112
- advance(deltaTime) {
113
- var _a;
114
- if (!this.isRunning)
115
- return;
116
- let completed = false;
117
- if (this.duration && this.easing) {
118
- this.currentTime += deltaTime;
119
- const linearProgress = clamp(0, this.currentTime / this.duration, 1);
120
- completed = linearProgress >= 1;
121
- const easedProgress = completed ? 1 : this.easing(linearProgress);
122
- this.value = this.from + (this.to - this.from) * easedProgress;
123
- }
124
- else if (this.lerp) {
125
- this.value = damp(this.value, this.to, this.lerp * 60, deltaTime);
126
- if (Math.round(this.value) === this.to) {
127
- this.value = this.to;
128
- completed = true;
129
- }
130
- }
131
- else {
132
- // If no easing or lerp, just jump to the end value
133
- this.value = this.to;
134
- completed = true;
135
- }
136
- if (completed) {
137
- this.stop();
138
- }
139
- // Call the onUpdate callback with the current value and completed status
140
- (_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.call(this, this.value, completed);
141
- }
142
- /** Stop the animation */
143
- stop() {
144
- this.isRunning = false;
145
- }
146
- /**
147
- * Set up the animation from a starting value to an ending value
148
- * with optional parameters for lerping, duration, easing, and onUpdate callback
149
- *
150
- * @param from - The starting value
151
- * @param to - The ending value
152
- * @param options - Options for the animation
153
- */
154
- fromTo(from, to, { lerp = 0.1, duration = 1, easing = (t) => t, onStart, onUpdate, }) {
155
- this.from = this.value = from;
156
- this.to = to;
157
- this.lerp = lerp;
158
- this.duration = duration;
159
- this.easing = easing;
160
- this.currentTime = 0;
161
- this.isRunning = true;
162
- onStart === null || onStart === void 0 ? void 0 : onStart();
163
- this.onUpdate = onUpdate;
164
- }
165
- }
132
+ function debounce(callback, delay) {
133
+ let timer;
134
+ return function (...args) {
135
+ let context = this;
136
+ clearTimeout(timer);
137
+ timer = setTimeout(() => {
138
+ timer = undefined;
139
+ callback.apply(context, args);
140
+ }, delay);
141
+ };
142
+ }
166
143
 
167
- function debounce(callback, delay) {
168
- let timer;
169
- return function (...args) {
170
- let context = this;
171
- clearTimeout(timer);
172
- timer = setTimeout(() => {
173
- timer = undefined;
174
- callback.apply(context, args);
175
- }, delay);
176
- };
177
- }
144
+ /**
145
+ * Dimensions class to handle the size of the content and wrapper
146
+ *
147
+ * @example
148
+ * const dimensions = new Dimensions(wrapper, content)
149
+ * dimensions.on('resize', (e) => {
150
+ * console.log(e.width, e.height)
151
+ * })
152
+ */
153
+ class Dimensions {
154
+ constructor(wrapper, content, { autoResize = true, debounce: debounceValue = 250 } = {}) {
155
+ this.wrapper = wrapper;
156
+ this.content = content;
157
+ this.width = 0;
158
+ this.height = 0;
159
+ this.scrollHeight = 0;
160
+ this.scrollWidth = 0;
161
+ this.resize = () => {
162
+ this.onWrapperResize();
163
+ this.onContentResize();
164
+ };
165
+ this.onWrapperResize = () => {
166
+ if (this.wrapper instanceof Window) {
167
+ this.width = window.innerWidth;
168
+ this.height = window.innerHeight;
169
+ }
170
+ else {
171
+ this.width = this.wrapper.clientWidth;
172
+ this.height = this.wrapper.clientHeight;
173
+ }
174
+ };
175
+ this.onContentResize = () => {
176
+ if (this.wrapper instanceof Window) {
177
+ this.scrollHeight = this.content.scrollHeight;
178
+ this.scrollWidth = this.content.scrollWidth;
179
+ }
180
+ else {
181
+ this.scrollHeight = this.wrapper.scrollHeight;
182
+ this.scrollWidth = this.wrapper.scrollWidth;
183
+ }
184
+ };
185
+ if (autoResize) {
186
+ this.debouncedResize = debounce(this.resize, debounceValue);
187
+ if (this.wrapper instanceof Window) {
188
+ window.addEventListener('resize', this.debouncedResize, false);
189
+ }
190
+ else {
191
+ this.wrapperResizeObserver = new ResizeObserver(this.debouncedResize);
192
+ this.wrapperResizeObserver.observe(this.wrapper);
193
+ }
194
+ this.contentResizeObserver = new ResizeObserver(this.debouncedResize);
195
+ this.contentResizeObserver.observe(this.content);
196
+ }
197
+ this.resize();
198
+ }
199
+ destroy() {
200
+ var _a, _b;
201
+ (_a = this.wrapperResizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
202
+ (_b = this.contentResizeObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
203
+ if (this.wrapper === window && this.debouncedResize) {
204
+ window.removeEventListener('resize', this.debouncedResize, false);
205
+ }
206
+ }
207
+ get limit() {
208
+ return {
209
+ x: this.scrollWidth - this.width,
210
+ y: this.scrollHeight - this.height,
211
+ };
212
+ }
213
+ }
178
214
 
179
- /**
180
- * Dimensions class to handle the size of the content and wrapper
181
- *
182
- * @example
183
- * const dimensions = new Dimensions(wrapper, content)
184
- * dimensions.on('resize', (e) => {
185
- * console.log(e.width, e.height)
186
- * })
187
- */
188
- class Dimensions {
189
- constructor(wrapper, content, { autoResize = true, debounce: debounceValue = 250 } = {}) {
190
- this.wrapper = wrapper;
191
- this.content = content;
192
- this.width = 0;
193
- this.height = 0;
194
- this.scrollHeight = 0;
195
- this.scrollWidth = 0;
196
- this.resize = () => {
197
- this.onWrapperResize();
198
- this.onContentResize();
199
- };
200
- this.onWrapperResize = () => {
201
- if (this.wrapper instanceof Window) {
202
- this.width = window.innerWidth;
203
- this.height = window.innerHeight;
204
- }
205
- else {
206
- this.width = this.wrapper.clientWidth;
207
- this.height = this.wrapper.clientHeight;
208
- }
209
- };
210
- this.onContentResize = () => {
211
- if (this.wrapper instanceof Window) {
212
- this.scrollHeight = this.content.scrollHeight;
213
- this.scrollWidth = this.content.scrollWidth;
214
- }
215
- else {
216
- this.scrollHeight = this.wrapper.scrollHeight;
217
- this.scrollWidth = this.wrapper.scrollWidth;
218
- }
219
- };
220
- if (autoResize) {
221
- this.debouncedResize = debounce(this.resize, debounceValue);
222
- if (this.wrapper instanceof Window) {
223
- window.addEventListener('resize', this.debouncedResize, false);
224
- }
225
- else {
226
- this.wrapperResizeObserver = new ResizeObserver(this.debouncedResize);
227
- this.wrapperResizeObserver.observe(this.wrapper);
228
- }
229
- this.contentResizeObserver = new ResizeObserver(this.debouncedResize);
230
- this.contentResizeObserver.observe(this.content);
231
- }
232
- this.resize();
233
- }
234
- destroy() {
235
- var _a, _b;
236
- (_a = this.wrapperResizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
237
- (_b = this.contentResizeObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
238
- if (this.wrapper === window && this.debouncedResize) {
239
- window.removeEventListener('resize', this.debouncedResize, false);
240
- }
241
- }
242
- get limit() {
243
- return {
244
- x: this.scrollWidth - this.width,
245
- y: this.scrollHeight - this.height,
246
- };
247
- }
248
- }
215
+ /**
216
+ * Emitter class to handle events
217
+ * @example
218
+ * const emitter = new Emitter()
219
+ * emitter.on('event', (data) => {
220
+ * console.log(data)
221
+ * })
222
+ * emitter.emit('event', 'data')
223
+ */
224
+ class Emitter {
225
+ constructor() {
226
+ this.events = {};
227
+ }
228
+ /**
229
+ * Emit an event with the given data
230
+ * @param event Event name
231
+ * @param args Data to pass to the event handlers
232
+ */
233
+ emit(event, ...args) {
234
+ var _a;
235
+ let callbacks = this.events[event] || [];
236
+ for (let i = 0, length = callbacks.length; i < length; i++) {
237
+ (_a = callbacks[i]) === null || _a === void 0 ? void 0 : _a.call(callbacks, ...args);
238
+ }
239
+ }
240
+ /**
241
+ * Add a callback to the event
242
+ * @param event Event name
243
+ * @param cb Callback function
244
+ * @returns Unsubscribe function
245
+ */
246
+ on(event, cb) {
247
+ var _a;
248
+ // Add the callback to the event's callback list, or create a new list with the callback
249
+ ((_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.push(cb)) || (this.events[event] = [cb]);
250
+ // Return an unsubscribe function
251
+ return () => {
252
+ var _a;
253
+ this.events[event] = (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.filter((i) => cb !== i);
254
+ };
255
+ }
256
+ /**
257
+ * Remove a callback from the event
258
+ * @param event Event name
259
+ * @param callback Callback function
260
+ */
261
+ off(event, callback) {
262
+ var _a;
263
+ this.events[event] = (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.filter((i) => callback !== i);
264
+ }
265
+ /**
266
+ * Remove all event listeners and clean up
267
+ */
268
+ destroy() {
269
+ this.events = {};
270
+ }
271
+ }
249
272
 
250
- /**
251
- * Emitter class to handle events
252
- * @example
253
- * const emitter = new Emitter()
254
- * emitter.on('event', (data) => {
255
- * console.log(data)
256
- * })
257
- * emitter.emit('event', 'data')
258
- */
259
- class Emitter {
260
- constructor() {
261
- this.events = {};
262
- }
263
- /**
264
- * Emit an event with the given data
265
- * @param event Event name
266
- * @param args Data to pass to the event handlers
267
- */
268
- emit(event, ...args) {
269
- var _a;
270
- let callbacks = this.events[event] || [];
271
- for (let i = 0, length = callbacks.length; i < length; i++) {
272
- (_a = callbacks[i]) === null || _a === void 0 ? void 0 : _a.call(callbacks, ...args);
273
- }
274
- }
275
- /**
276
- * Add a callback to the event
277
- * @param event Event name
278
- * @param cb Callback function
279
- * @returns Unsubscribe function
280
- */
281
- on(event, cb) {
282
- var _a;
283
- // Add the callback to the event's callback list, or create a new list with the callback
284
- ((_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.push(cb)) || (this.events[event] = [cb]);
285
- // Return an unsubscribe function
286
- return () => {
287
- var _a;
288
- this.events[event] = (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.filter((i) => cb !== i);
289
- };
290
- }
291
- /**
292
- * Remove a callback from the event
293
- * @param event Event name
294
- * @param callback Callback function
295
- */
296
- off(event, callback) {
297
- var _a;
298
- this.events[event] = (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.filter((i) => callback !== i);
299
- }
300
- /**
301
- * Remove all event listeners and clean up
302
- */
303
- destroy() {
304
- this.events = {};
305
- }
306
- }
273
+ const LINE_HEIGHT = 100 / 6;
274
+ const listenerOptions = { passive: false };
275
+ class VirtualScroll {
276
+ constructor(element, options = { wheelMultiplier: 1, touchMultiplier: 1 }) {
277
+ this.element = element;
278
+ this.options = options;
279
+ this.touchStart = {
280
+ x: 0,
281
+ y: 0,
282
+ };
283
+ this.lastDelta = {
284
+ x: 0,
285
+ y: 0,
286
+ };
287
+ this.window = {
288
+ width: 0,
289
+ height: 0,
290
+ };
291
+ this.emitter = new Emitter();
292
+ /**
293
+ * Event handler for 'touchstart' event
294
+ *
295
+ * @param event Touch event
296
+ */
297
+ this.onTouchStart = (event) => {
298
+ // @ts-expect-error - event.targetTouches is not defined
299
+ const { clientX, clientY } = event.targetTouches
300
+ ? event.targetTouches[0]
301
+ : event;
302
+ this.touchStart.x = clientX;
303
+ this.touchStart.y = clientY;
304
+ this.lastDelta = {
305
+ x: 0,
306
+ y: 0,
307
+ };
308
+ this.emitter.emit('scroll', {
309
+ deltaX: 0,
310
+ deltaY: 0,
311
+ event,
312
+ });
313
+ };
314
+ /** Event handler for 'touchmove' event */
315
+ this.onTouchMove = (event) => {
316
+ // @ts-expect-error - event.targetTouches is not defined
317
+ const { clientX, clientY } = event.targetTouches
318
+ ? event.targetTouches[0]
319
+ : event;
320
+ const deltaX = -(clientX - this.touchStart.x) * this.options.touchMultiplier;
321
+ const deltaY = -(clientY - this.touchStart.y) * this.options.touchMultiplier;
322
+ this.touchStart.x = clientX;
323
+ this.touchStart.y = clientY;
324
+ this.lastDelta = {
325
+ x: deltaX,
326
+ y: deltaY,
327
+ };
328
+ this.emitter.emit('scroll', {
329
+ deltaX,
330
+ deltaY,
331
+ event,
332
+ });
333
+ };
334
+ this.onTouchEnd = (event) => {
335
+ this.emitter.emit('scroll', {
336
+ deltaX: this.lastDelta.x,
337
+ deltaY: this.lastDelta.y,
338
+ event,
339
+ });
340
+ };
341
+ /** Event handler for 'wheel' event */
342
+ this.onWheel = (event) => {
343
+ let { deltaX, deltaY, deltaMode } = event;
344
+ const multiplierX = deltaMode === 1 ? LINE_HEIGHT : deltaMode === 2 ? this.window.width : 1;
345
+ const multiplierY = deltaMode === 1 ? LINE_HEIGHT : deltaMode === 2 ? this.window.height : 1;
346
+ deltaX *= multiplierX;
347
+ deltaY *= multiplierY;
348
+ deltaX *= this.options.wheelMultiplier;
349
+ deltaY *= this.options.wheelMultiplier;
350
+ this.emitter.emit('scroll', { deltaX, deltaY, event });
351
+ };
352
+ this.onWindowResize = () => {
353
+ this.window = {
354
+ width: window.innerWidth,
355
+ height: window.innerHeight,
356
+ };
357
+ };
358
+ window.addEventListener('resize', this.onWindowResize, false);
359
+ this.onWindowResize();
360
+ this.element.addEventListener('wheel', this.onWheel, listenerOptions);
361
+ this.element.addEventListener('touchstart', this.onTouchStart, listenerOptions);
362
+ this.element.addEventListener('touchmove', this.onTouchMove, listenerOptions);
363
+ this.element.addEventListener('touchend', this.onTouchEnd, listenerOptions);
364
+ }
365
+ /**
366
+ * Add an event listener for the given event and callback
367
+ *
368
+ * @param event Event name
369
+ * @param callback Callback function
370
+ */
371
+ on(event, callback) {
372
+ return this.emitter.on(event, callback);
373
+ }
374
+ /** Remove all event listeners and clean up */
375
+ destroy() {
376
+ this.emitter.destroy();
377
+ window.removeEventListener('resize', this.onWindowResize, false);
378
+ this.element.removeEventListener('wheel', this.onWheel, listenerOptions);
379
+ this.element.removeEventListener('touchstart', this.onTouchStart, listenerOptions);
380
+ this.element.removeEventListener('touchmove', this.onTouchMove, listenerOptions);
381
+ this.element.removeEventListener('touchend', this.onTouchEnd, listenerOptions);
382
+ }
383
+ }
307
384
 
308
- const LINE_HEIGHT = 100 / 6;
309
- const listenerOptions = { passive: false };
310
- class VirtualScroll {
311
- constructor(element, options = { wheelMultiplier: 1, touchMultiplier: 1 }) {
312
- this.element = element;
313
- this.options = options;
314
- this.touchStart = {
315
- x: 0,
316
- y: 0,
317
- };
318
- this.lastDelta = {
319
- x: 0,
320
- y: 0,
321
- };
322
- this.window = {
323
- width: 0,
324
- height: 0,
325
- };
326
- this.emitter = new Emitter();
327
- /**
328
- * Event handler for 'touchstart' event
329
- *
330
- * @param event Touch event
331
- */
332
- this.onTouchStart = (event) => {
333
- // @ts-expect-error - event.targetTouches is not defined
334
- const { clientX, clientY } = event.targetTouches
335
- ? event.targetTouches[0]
336
- : event;
337
- this.touchStart.x = clientX;
338
- this.touchStart.y = clientY;
339
- this.lastDelta = {
340
- x: 0,
341
- y: 0,
342
- };
343
- this.emitter.emit('scroll', {
344
- deltaX: 0,
345
- deltaY: 0,
346
- event,
347
- });
348
- };
349
- /** Event handler for 'touchmove' event */
350
- this.onTouchMove = (event) => {
351
- // @ts-expect-error - event.targetTouches is not defined
352
- const { clientX, clientY } = event.targetTouches
353
- ? event.targetTouches[0]
354
- : event;
355
- const deltaX = -(clientX - this.touchStart.x) * this.options.touchMultiplier;
356
- const deltaY = -(clientY - this.touchStart.y) * this.options.touchMultiplier;
357
- this.touchStart.x = clientX;
358
- this.touchStart.y = clientY;
359
- this.lastDelta = {
360
- x: deltaX,
361
- y: deltaY,
362
- };
363
- this.emitter.emit('scroll', {
364
- deltaX,
365
- deltaY,
366
- event,
367
- });
368
- };
369
- this.onTouchEnd = (event) => {
370
- this.emitter.emit('scroll', {
371
- deltaX: this.lastDelta.x,
372
- deltaY: this.lastDelta.y,
373
- event,
374
- });
375
- };
376
- /** Event handler for 'wheel' event */
377
- this.onWheel = (event) => {
378
- let { deltaX, deltaY, deltaMode } = event;
379
- const multiplierX = deltaMode === 1 ? LINE_HEIGHT : deltaMode === 2 ? this.window.width : 1;
380
- const multiplierY = deltaMode === 1 ? LINE_HEIGHT : deltaMode === 2 ? this.window.height : 1;
381
- deltaX *= multiplierX;
382
- deltaY *= multiplierY;
383
- deltaX *= this.options.wheelMultiplier;
384
- deltaY *= this.options.wheelMultiplier;
385
- this.emitter.emit('scroll', { deltaX, deltaY, event });
386
- };
387
- this.onWindowResize = () => {
388
- this.window = {
389
- width: window.innerWidth,
390
- height: window.innerHeight,
391
- };
392
- };
393
- window.addEventListener('resize', this.onWindowResize, false);
394
- this.onWindowResize();
395
- this.element.addEventListener('wheel', this.onWheel, listenerOptions);
396
- this.element.addEventListener('touchstart', this.onTouchStart, listenerOptions);
397
- this.element.addEventListener('touchmove', this.onTouchMove, listenerOptions);
398
- this.element.addEventListener('touchend', this.onTouchEnd, listenerOptions);
399
- }
400
- /**
401
- * Add an event listener for the given event and callback
402
- *
403
- * @param event Event name
404
- * @param callback Callback function
405
- */
406
- on(event, callback) {
407
- return this.emitter.on(event, callback);
408
- }
409
- /** Remove all event listeners and clean up */
410
- destroy() {
411
- this.emitter.destroy();
412
- window.removeEventListener('resize', this.onWindowResize, false);
413
- this.element.removeEventListener('wheel', this.onWheel, listenerOptions);
414
- this.element.removeEventListener('touchstart', this.onTouchStart, listenerOptions);
415
- this.element.removeEventListener('touchmove', this.onTouchMove, listenerOptions);
416
- this.element.removeEventListener('touchend', this.onTouchEnd, listenerOptions);
417
- }
418
- }
385
+ class Lenis {
386
+ constructor({ wrapper = window, content = document.documentElement, eventsTarget = wrapper, smoothWheel = true, syncTouch = false, syncTouchLerp = 0.075, touchInertiaMultiplier = 35, duration, // in seconds
387
+ easing = (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), lerp = 0.1, infinite = false, orientation = 'vertical', // vertical, horizontal
388
+ gestureOrientation = 'vertical', // vertical, horizontal, both
389
+ touchMultiplier = 1, wheelMultiplier = 1, autoResize = true, prevent, virtualScroll, __experimental__naiveDimensions = false, } = {}) {
390
+ this._isScrolling = false; // true when scroll is animating
391
+ this._isStopped = false; // true if user should not be able to scroll - enable/disable programmatically
392
+ this._isLocked = false; // same as isStopped but enabled/disabled when scroll reaches target
393
+ this._preventNextNativeScrollEvent = false;
394
+ this._resetVelocityTimeout = null;
395
+ /**
396
+ * The time in ms since the lenis instance was created
397
+ */
398
+ this.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
+ this.userData = {};
410
+ /**
411
+ * The last velocity of the scroll
412
+ */
413
+ this.lastVelocity = 0;
414
+ /**
415
+ * The current velocity of the scroll
416
+ */
417
+ this.velocity = 0;
418
+ /**
419
+ * The direction of the scroll
420
+ */
421
+ this.direction = 0;
422
+ // These are instanciated here as they don't need information from the options
423
+ this.animate = new Animate();
424
+ this.emitter = new Emitter();
425
+ this.onPointerDown = (event) => {
426
+ if (event.button === 1) {
427
+ this.reset();
428
+ }
429
+ };
430
+ this.onVirtualScroll = (data) => {
431
+ if (typeof this.options.virtualScroll === 'function' &&
432
+ this.options.virtualScroll(data) === false)
433
+ return;
434
+ const { deltaX, deltaY, event } = data;
435
+ this.emitter.emit('virtual-scroll', { deltaX, deltaY, event });
436
+ // keep zoom feature
437
+ if (event.ctrlKey)
438
+ return;
439
+ const isTouch = event.type.includes('touch');
440
+ const isWheel = event.type.includes('wheel');
441
+ this.isTouching = event.type === 'touchstart' || event.type === 'touchmove';
442
+ // if (event.type === 'touchend') {
443
+ // console.log('touchend', this.scroll)
444
+ // // this.lastVelocity = this.velocity
445
+ // // this.velocity = 0
446
+ // // this.isScrolling = false
447
+ // this.emit({ type: 'touchend' })
448
+ // // alert('touchend')
449
+ // return
450
+ // }
451
+ const isTapToStop = this.options.syncTouch &&
452
+ isTouch &&
453
+ event.type === 'touchstart' &&
454
+ !this.isStopped &&
455
+ !this.isLocked;
456
+ if (isTapToStop) {
457
+ this.reset();
458
+ return;
459
+ }
460
+ const isClick = deltaX === 0 && deltaY === 0; // click event
461
+ // const isPullToRefresh =
462
+ // this.options.gestureOrientation === 'vertical' &&
463
+ // this.scroll === 0 &&
464
+ // !this.options.infinite &&
465
+ // deltaY <= 5 // touch pull to refresh, not reliable yet
466
+ const isUnknownGesture = (this.options.gestureOrientation === 'vertical' && deltaY === 0) ||
467
+ (this.options.gestureOrientation === 'horizontal' && deltaX === 0);
468
+ if (isClick || isUnknownGesture) {
469
+ // console.log('prevent')
470
+ return;
471
+ }
472
+ // catch if scrolling on nested scroll elements
473
+ let composedPath = event.composedPath();
474
+ composedPath = composedPath.slice(0, composedPath.indexOf(this.rootElement)); // remove parents elements
475
+ const prevent = this.options.prevent;
476
+ if (!!composedPath.find((node) => {
477
+ var _a, _b, _c, _d, _e;
478
+ return node instanceof HTMLElement &&
479
+ ((typeof prevent === 'function' && (prevent === null || prevent === void 0 ? void 0 : prevent(node))) ||
480
+ ((_a = node.hasAttribute) === null || _a === void 0 ? void 0 : _a.call(node, 'data-lenis-prevent')) ||
481
+ (isTouch && ((_b = node.hasAttribute) === null || _b === void 0 ? void 0 : _b.call(node, 'data-lenis-prevent-touch'))) ||
482
+ (isWheel && ((_c = node.hasAttribute) === null || _c === void 0 ? void 0 : _c.call(node, 'data-lenis-prevent-wheel'))) ||
483
+ (((_d = node.classList) === null || _d === void 0 ? void 0 : _d.contains('lenis')) &&
484
+ !((_e = node.classList) === null || _e === void 0 ? void 0 : _e.contains('lenis-stopped'))));
485
+ } // nested lenis instance
486
+ ))
487
+ return;
488
+ if (this.isStopped || this.isLocked) {
489
+ event.preventDefault(); // this will stop forwarding the event to the parent, this is problematic
490
+ return;
491
+ }
492
+ const isSmooth = (this.options.syncTouch && isTouch) ||
493
+ (this.options.smoothWheel && isWheel);
494
+ if (!isSmooth) {
495
+ this.isScrolling = 'native';
496
+ this.animate.stop();
497
+ return;
498
+ }
499
+ event.preventDefault();
500
+ let delta = deltaY;
501
+ if (this.options.gestureOrientation === 'both') {
502
+ delta = Math.abs(deltaY) > Math.abs(deltaX) ? deltaY : deltaX;
503
+ }
504
+ else if (this.options.gestureOrientation === 'horizontal') {
505
+ delta = deltaX;
506
+ }
507
+ const syncTouch = isTouch && this.options.syncTouch;
508
+ const isTouchEnd = isTouch && event.type === 'touchend';
509
+ const hasTouchInertia = isTouchEnd && Math.abs(delta) > 5;
510
+ if (hasTouchInertia) {
511
+ delta = this.velocity * this.options.touchInertiaMultiplier;
512
+ }
513
+ this.scrollTo(this.targetScroll + delta, Object.assign({ programmatic: false }, (syncTouch
514
+ ? {
515
+ lerp: hasTouchInertia ? this.options.syncTouchLerp : 1,
516
+ }
517
+ : {
518
+ lerp: this.options.lerp,
519
+ duration: this.options.duration,
520
+ easing: this.options.easing,
521
+ })));
522
+ };
523
+ this.onNativeScroll = () => {
524
+ if (this._resetVelocityTimeout !== null) {
525
+ clearTimeout(this._resetVelocityTimeout);
526
+ this._resetVelocityTimeout = null;
527
+ }
528
+ if (this._preventNextNativeScrollEvent) {
529
+ this._preventNextNativeScrollEvent = false;
530
+ return;
531
+ }
532
+ if (this.isScrolling === false || this.isScrolling === 'native') {
533
+ const lastScroll = this.animatedScroll;
534
+ this.animatedScroll = this.targetScroll = this.actualScroll;
535
+ this.lastVelocity = this.velocity;
536
+ this.velocity = this.animatedScroll - lastScroll;
537
+ this.direction = Math.sign(this.animatedScroll - lastScroll);
538
+ this.isScrolling = 'native';
539
+ this.emit();
540
+ if (this.velocity !== 0) {
541
+ this._resetVelocityTimeout = setTimeout(() => {
542
+ this.lastVelocity = this.velocity;
543
+ this.velocity = 0;
544
+ this.isScrolling = false;
545
+ this.emit();
546
+ }, 400);
547
+ }
548
+ }
549
+ };
550
+ // Set version
551
+ window.lenisVersion = version;
552
+ // Check if wrapper is html or body, fallback to window
553
+ if (!wrapper ||
554
+ wrapper === document.documentElement ||
555
+ wrapper === document.body) {
556
+ wrapper = window;
557
+ }
558
+ // Setup options
559
+ this.options = {
560
+ wrapper,
561
+ content,
562
+ eventsTarget,
563
+ smoothWheel,
564
+ syncTouch,
565
+ syncTouchLerp,
566
+ touchInertiaMultiplier,
567
+ duration,
568
+ easing,
569
+ lerp,
570
+ infinite,
571
+ gestureOrientation,
572
+ orientation,
573
+ touchMultiplier,
574
+ wheelMultiplier,
575
+ autoResize,
576
+ prevent,
577
+ virtualScroll,
578
+ __experimental__naiveDimensions,
579
+ };
580
+ // Setup dimensions instance
581
+ this.dimensions = new Dimensions(wrapper, content, { autoResize });
582
+ // Setup class name
583
+ this.updateClassName();
584
+ // Set the initial scroll value for all scroll information
585
+ this.targetScroll = this.animatedScroll = this.actualScroll;
586
+ // Add event listeners
587
+ this.options.wrapper.addEventListener('scroll', this.onNativeScroll, false);
588
+ this.options.wrapper.addEventListener('pointerdown', this.onPointerDown, false);
589
+ // Setup virtual scroll instance
590
+ this.virtualScroll = new VirtualScroll(eventsTarget, {
591
+ touchMultiplier,
592
+ wheelMultiplier,
593
+ });
594
+ this.virtualScroll.on('scroll', this.onVirtualScroll);
595
+ }
596
+ /**
597
+ * Destroy the lenis instance, remove all event listeners and clean up the class name
598
+ */
599
+ destroy() {
600
+ this.emitter.destroy();
601
+ this.options.wrapper.removeEventListener('scroll', this.onNativeScroll, false);
602
+ this.options.wrapper.removeEventListener('pointerdown', this.onPointerDown, false);
603
+ this.virtualScroll.destroy();
604
+ this.dimensions.destroy();
605
+ this.cleanUpClassName();
606
+ }
607
+ on(event, callback) {
608
+ return this.emitter.on(event, callback);
609
+ }
610
+ off(event, callback) {
611
+ return this.emitter.off(event, callback);
612
+ }
613
+ setScroll(scroll) {
614
+ // apply scroll value immediately
615
+ if (this.isHorizontal) {
616
+ this.rootElement.scrollLeft = scroll;
617
+ }
618
+ else {
619
+ this.rootElement.scrollTop = scroll;
620
+ }
621
+ }
622
+ /**
623
+ * Force lenis to recalculate the dimensions
624
+ */
625
+ resize() {
626
+ this.dimensions.resize();
627
+ this.animatedScroll = this.targetScroll = this.actualScroll;
628
+ this.emit();
629
+ }
630
+ emit() {
631
+ this.emitter.emit('scroll', this);
632
+ }
633
+ reset() {
634
+ this.isLocked = false;
635
+ this.isScrolling = false;
636
+ this.animatedScroll = this.targetScroll = this.actualScroll;
637
+ this.lastVelocity = this.velocity = 0;
638
+ this.animate.stop();
639
+ }
640
+ /**
641
+ * Start lenis scroll after it has been stopped
642
+ */
643
+ start() {
644
+ if (!this.isStopped)
645
+ return;
646
+ this.isStopped = false;
647
+ this.reset();
648
+ }
649
+ /**
650
+ * Stop lenis scroll
651
+ */
652
+ stop() {
653
+ if (this.isStopped)
654
+ return;
655
+ this.isStopped = true;
656
+ this.animate.stop();
657
+ this.reset();
658
+ }
659
+ /**
660
+ * RequestAnimationFrame for lenis
661
+ *
662
+ * @param time The time in ms from an external clock like `requestAnimationFrame` or Tempus
663
+ */
664
+ raf(time) {
665
+ const deltaTime = time - (this.time || time);
666
+ this.time = time;
667
+ this.animate.advance(deltaTime * 0.001);
668
+ }
669
+ /**
670
+ * Scroll to a target value
671
+ *
672
+ * @param target The target value to scroll to
673
+ * @param options The options for the scroll
674
+ *
675
+ * @example
676
+ * lenis.scrollTo(100, {
677
+ * offset: 100,
678
+ * duration: 1,
679
+ * easing: (t) => 1 - Math.cos((t * Math.PI) / 2),
680
+ * lerp: 0.1,
681
+ * onStart: () => {
682
+ * console.log('onStart')
683
+ * },
684
+ * onComplete: () => {
685
+ * console.log('onComplete')
686
+ * },
687
+ * })
688
+ */
689
+ scrollTo(target, { offset = 0, immediate = false, lock = false, duration = this.options.duration, easing = this.options.easing, lerp = this.options.lerp, onStart, onComplete, force = false, // scroll even if stopped
690
+ programmatic = true, // called from outside of the class
691
+ userData, } = {}) {
692
+ if ((this.isStopped || this.isLocked) && !force)
693
+ return;
694
+ // keywords
695
+ if (typeof target === 'string' &&
696
+ ['top', 'left', 'start'].includes(target)) {
697
+ target = 0;
698
+ }
699
+ else if (typeof target === 'string' &&
700
+ ['bottom', 'right', 'end'].includes(target)) {
701
+ target = this.limit;
702
+ }
703
+ else {
704
+ let node;
705
+ if (typeof target === 'string') {
706
+ // CSS selector
707
+ node = document.querySelector(target);
708
+ }
709
+ else if (target instanceof HTMLElement && (target === null || target === void 0 ? void 0 : target.nodeType)) {
710
+ // Node element
711
+ node = target;
712
+ }
713
+ if (node) {
714
+ if (this.options.wrapper !== window) {
715
+ // nested scroll offset correction
716
+ const wrapperRect = this.rootElement.getBoundingClientRect();
717
+ offset -= this.isHorizontal ? wrapperRect.left : wrapperRect.top;
718
+ }
719
+ const rect = node.getBoundingClientRect();
720
+ target =
721
+ (this.isHorizontal ? rect.left : rect.top) + this.animatedScroll;
722
+ }
723
+ }
724
+ if (typeof target !== 'number')
725
+ return;
726
+ target += offset;
727
+ target = Math.round(target);
728
+ if (this.options.infinite) {
729
+ if (programmatic) {
730
+ this.targetScroll = this.animatedScroll = this.scroll;
731
+ }
732
+ }
733
+ else {
734
+ target = clamp(0, target, this.limit);
735
+ }
736
+ if (target === this.targetScroll) {
737
+ onStart === null || onStart === void 0 ? void 0 : onStart(this);
738
+ onComplete === null || onComplete === void 0 ? void 0 : onComplete(this);
739
+ return;
740
+ }
741
+ this.userData = userData !== null && userData !== void 0 ? userData : {};
742
+ if (immediate) {
743
+ this.animatedScroll = this.targetScroll = target;
744
+ this.setScroll(this.scroll);
745
+ this.reset();
746
+ this.preventNextNativeScrollEvent();
747
+ this.emit();
748
+ onComplete === null || onComplete === void 0 ? void 0 : onComplete(this);
749
+ this.userData = {};
750
+ return;
751
+ }
752
+ if (!programmatic) {
753
+ this.targetScroll = target;
754
+ }
755
+ this.animate.fromTo(this.animatedScroll, target, {
756
+ duration,
757
+ easing,
758
+ lerp,
759
+ onStart: () => {
760
+ // started
761
+ if (lock)
762
+ this.isLocked = true;
763
+ this.isScrolling = 'smooth';
764
+ onStart === null || onStart === void 0 ? void 0 : onStart(this);
765
+ },
766
+ onUpdate: (value, completed) => {
767
+ this.isScrolling = 'smooth';
768
+ // updated
769
+ this.lastVelocity = this.velocity;
770
+ this.velocity = value - this.animatedScroll;
771
+ this.direction = Math.sign(this.velocity);
772
+ this.animatedScroll = value;
773
+ this.setScroll(this.scroll);
774
+ if (programmatic) {
775
+ // wheel during programmatic should stop it
776
+ this.targetScroll = value;
777
+ }
778
+ if (!completed)
779
+ this.emit();
780
+ if (completed) {
781
+ this.reset();
782
+ this.emit();
783
+ onComplete === null || onComplete === void 0 ? void 0 : onComplete(this);
784
+ this.userData = {};
785
+ // avoid emitting event twice
786
+ this.preventNextNativeScrollEvent();
787
+ }
788
+ },
789
+ });
790
+ }
791
+ preventNextNativeScrollEvent() {
792
+ this._preventNextNativeScrollEvent = true;
793
+ requestAnimationFrame(() => {
794
+ this._preventNextNativeScrollEvent = false;
795
+ });
796
+ }
797
+ /**
798
+ * The root element on which lenis is instanced
799
+ */
800
+ get rootElement() {
801
+ return (this.options.wrapper === window
802
+ ? document.documentElement
803
+ : this.options.wrapper);
804
+ }
805
+ /**
806
+ * The limit which is the maximum scroll value
807
+ */
808
+ get limit() {
809
+ if (this.options.__experimental__naiveDimensions) {
810
+ if (this.isHorizontal) {
811
+ return this.rootElement.scrollWidth - this.rootElement.clientWidth;
812
+ }
813
+ else {
814
+ return this.rootElement.scrollHeight - this.rootElement.clientHeight;
815
+ }
816
+ }
817
+ else {
818
+ return this.dimensions.limit[this.isHorizontal ? 'x' : 'y'];
819
+ }
820
+ }
821
+ /**
822
+ * Whether or not the scroll is horizontal
823
+ */
824
+ get isHorizontal() {
825
+ return this.options.orientation === 'horizontal';
826
+ }
827
+ /**
828
+ * The actual scroll value
829
+ */
830
+ get actualScroll() {
831
+ // value browser takes into account
832
+ return this.isHorizontal
833
+ ? this.rootElement.scrollLeft
834
+ : this.rootElement.scrollTop;
835
+ }
836
+ /**
837
+ * The current scroll value
838
+ */
839
+ get scroll() {
840
+ return this.options.infinite
841
+ ? modulo(this.animatedScroll, this.limit)
842
+ : this.animatedScroll;
843
+ }
844
+ /**
845
+ * The progress of the scroll relative to the limit
846
+ */
847
+ get progress() {
848
+ // avoid progress to be NaN
849
+ return this.limit === 0 ? 1 : this.scroll / this.limit;
850
+ }
851
+ /**
852
+ * Current scroll state
853
+ */
854
+ get isScrolling() {
855
+ return this._isScrolling;
856
+ }
857
+ set isScrolling(value) {
858
+ if (this._isScrolling !== value) {
859
+ this._isScrolling = value;
860
+ this.updateClassName();
861
+ }
862
+ }
863
+ /**
864
+ * Check if lenis is stopped
865
+ */
866
+ get isStopped() {
867
+ return this._isStopped;
868
+ }
869
+ set isStopped(value) {
870
+ if (this._isStopped !== value) {
871
+ this._isStopped = value;
872
+ this.updateClassName();
873
+ }
874
+ }
875
+ /**
876
+ * Check if lenis is locked
877
+ */
878
+ get isLocked() {
879
+ return this._isLocked;
880
+ }
881
+ set isLocked(value) {
882
+ if (this._isLocked !== value) {
883
+ this._isLocked = value;
884
+ this.updateClassName();
885
+ }
886
+ }
887
+ /**
888
+ * Check if lenis is smooth scrolling
889
+ */
890
+ get isSmooth() {
891
+ return this.isScrolling === 'smooth';
892
+ }
893
+ /**
894
+ * The class name applied to the wrapper element
895
+ */
896
+ get className() {
897
+ let className = 'lenis';
898
+ if (this.isStopped)
899
+ className += ' lenis-stopped';
900
+ if (this.isLocked)
901
+ className += ' lenis-locked';
902
+ if (this.isScrolling)
903
+ className += ' lenis-scrolling';
904
+ if (this.isScrolling === 'smooth')
905
+ className += ' lenis-smooth';
906
+ return className;
907
+ }
908
+ updateClassName() {
909
+ this.cleanUpClassName();
910
+ this.rootElement.className =
911
+ `${this.rootElement.className} ${this.className}`.trim();
912
+ }
913
+ cleanUpClassName() {
914
+ this.rootElement.className = this.rootElement.className
915
+ .replace(/lenis(-\w+)?/g, '')
916
+ .trim();
917
+ }
918
+ }
419
919
 
420
- var _Lenis_isScrolling, _Lenis_isStopped, _Lenis_isLocked, _Lenis_preventNextNativeScrollEvent, _Lenis_resetVelocityTimeout;
421
- class Lenis {
422
- constructor({ wrapper = window, content = document.documentElement, eventsTarget = wrapper, smoothWheel = true, syncTouch = false, syncTouchLerp = 0.075, touchInertiaMultiplier = 35, duration, // in seconds
423
- easing = (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), lerp = 0.1, infinite = false, orientation = 'vertical', // vertical, horizontal
424
- gestureOrientation = 'vertical', // vertical, horizontal, both
425
- touchMultiplier = 1, wheelMultiplier = 1, autoResize = true, prevent, virtualScroll, __experimental__naiveDimensions = false, } = {}) {
426
- _Lenis_isScrolling.set(this, false); // true when scroll is animating
427
- _Lenis_isStopped.set(this, false); // true if user should not be able to scroll - enable/disable programmatically
428
- _Lenis_isLocked.set(this, false); // same as isStopped but enabled/disabled when scroll reaches target
429
- _Lenis_preventNextNativeScrollEvent.set(this, false);
430
- _Lenis_resetVelocityTimeout.set(this, null
431
- /**
432
- * Whether or not the user is touching the screen
433
- */
434
- );
435
- /**
436
- * The time in ms since the lenis instance was created
437
- */
438
- this.time = 0;
439
- /**
440
- * User data that will be forwarded through the scroll event
441
- *
442
- * @example
443
- * lenis.scrollTo(100, {
444
- * userData: {
445
- * foo: 'bar'
446
- * }
447
- * })
448
- */
449
- this.userData = {};
450
- /**
451
- * The last velocity of the scroll
452
- */
453
- this.lastVelocity = 0;
454
- /**
455
- * The current velocity of the scroll
456
- */
457
- this.velocity = 0;
458
- /**
459
- * The direction of the scroll
460
- */
461
- this.direction = 0;
462
- // These are instanciated here as they don't need information from the options
463
- this.animate = new Animate();
464
- this.emitter = new Emitter();
465
- this.onPointerDown = (event) => {
466
- if (event.button === 1) {
467
- this.reset();
468
- }
469
- };
470
- this.onVirtualScroll = (data) => {
471
- if (typeof this.options.virtualScroll === 'function' &&
472
- this.options.virtualScroll(data) === false)
473
- return;
474
- const { deltaX, deltaY, event } = data;
475
- this.emitter.emit('virtual-scroll', { deltaX, deltaY, event });
476
- // keep zoom feature
477
- if (event.ctrlKey)
478
- return;
479
- const isTouch = event.type.includes('touch');
480
- const isWheel = event.type.includes('wheel');
481
- this.isTouching = event.type === 'touchstart' || event.type === 'touchmove';
482
- // if (event.type === 'touchend') {
483
- // console.log('touchend', this.scroll)
484
- // // this.lastVelocity = this.velocity
485
- // // this.velocity = 0
486
- // // this.isScrolling = false
487
- // this.emit({ type: 'touchend' })
488
- // // alert('touchend')
489
- // return
490
- // }
491
- const isTapToStop = this.options.syncTouch &&
492
- isTouch &&
493
- event.type === 'touchstart' &&
494
- !this.isStopped &&
495
- !this.isLocked;
496
- if (isTapToStop) {
497
- this.reset();
498
- return;
499
- }
500
- const isClick = deltaX === 0 && deltaY === 0; // click event
501
- // const isPullToRefresh =
502
- // this.options.gestureOrientation === 'vertical' &&
503
- // this.scroll === 0 &&
504
- // !this.options.infinite &&
505
- // deltaY <= 5 // touch pull to refresh, not reliable yet
506
- const isUnknownGesture = (this.options.gestureOrientation === 'vertical' && deltaY === 0) ||
507
- (this.options.gestureOrientation === 'horizontal' && deltaX === 0);
508
- if (isClick || isUnknownGesture) {
509
- // console.log('prevent')
510
- return;
511
- }
512
- // catch if scrolling on nested scroll elements
513
- let composedPath = event.composedPath();
514
- composedPath = composedPath.slice(0, composedPath.indexOf(this.rootElement)); // remove parents elements
515
- const prevent = this.options.prevent;
516
- if (!!composedPath.find((node) => {
517
- var _a, _b, _c, _d, _e;
518
- return node instanceof HTMLElement &&
519
- ((typeof prevent === 'function' && (prevent === null || prevent === void 0 ? void 0 : prevent(node))) ||
520
- ((_a = node.hasAttribute) === null || _a === void 0 ? void 0 : _a.call(node, 'data-lenis-prevent')) ||
521
- (isTouch && ((_b = node.hasAttribute) === null || _b === void 0 ? void 0 : _b.call(node, 'data-lenis-prevent-touch'))) ||
522
- (isWheel && ((_c = node.hasAttribute) === null || _c === void 0 ? void 0 : _c.call(node, 'data-lenis-prevent-wheel'))) ||
523
- (((_d = node.classList) === null || _d === void 0 ? void 0 : _d.contains('lenis')) &&
524
- !((_e = node.classList) === null || _e === void 0 ? void 0 : _e.contains('lenis-stopped'))));
525
- } // nested lenis instance
526
- ))
527
- return;
528
- if (this.isStopped || this.isLocked) {
529
- event.preventDefault(); // this will stop forwarding the event to the parent, this is problematic
530
- return;
531
- }
532
- const isSmooth = (this.options.syncTouch && isTouch) ||
533
- (this.options.smoothWheel && isWheel);
534
- if (!isSmooth) {
535
- this.isScrolling = 'native';
536
- this.animate.stop();
537
- return;
538
- }
539
- event.preventDefault();
540
- let delta = deltaY;
541
- if (this.options.gestureOrientation === 'both') {
542
- delta = Math.abs(deltaY) > Math.abs(deltaX) ? deltaY : deltaX;
543
- }
544
- else if (this.options.gestureOrientation === 'horizontal') {
545
- delta = deltaX;
546
- }
547
- const syncTouch = isTouch && this.options.syncTouch;
548
- const isTouchEnd = isTouch && event.type === 'touchend';
549
- const hasTouchInertia = isTouchEnd && Math.abs(delta) > 5;
550
- if (hasTouchInertia) {
551
- delta = this.velocity * this.options.touchInertiaMultiplier;
552
- }
553
- this.scrollTo(this.targetScroll + delta, Object.assign({ programmatic: false }, (syncTouch
554
- ? {
555
- lerp: hasTouchInertia ? this.options.syncTouchLerp : 1,
556
- }
557
- : {
558
- lerp: this.options.lerp,
559
- duration: this.options.duration,
560
- easing: this.options.easing,
561
- })));
562
- };
563
- this.onNativeScroll = () => {
564
- if (__classPrivateFieldGet(this, _Lenis_resetVelocityTimeout, "f") !== null) {
565
- clearTimeout(__classPrivateFieldGet(this, _Lenis_resetVelocityTimeout, "f"));
566
- __classPrivateFieldSet(this, _Lenis_resetVelocityTimeout, null, "f");
567
- }
568
- if (__classPrivateFieldGet(this, _Lenis_preventNextNativeScrollEvent, "f")) {
569
- __classPrivateFieldSet(this, _Lenis_preventNextNativeScrollEvent, false, "f");
570
- return;
571
- }
572
- if (this.isScrolling === false || this.isScrolling === 'native') {
573
- const lastScroll = this.animatedScroll;
574
- this.animatedScroll = this.targetScroll = this.actualScroll;
575
- this.lastVelocity = this.velocity;
576
- this.velocity = this.animatedScroll - lastScroll;
577
- this.direction = Math.sign(this.animatedScroll - lastScroll);
578
- this.isScrolling = 'native';
579
- this.emit();
580
- if (this.velocity !== 0) {
581
- __classPrivateFieldSet(this, _Lenis_resetVelocityTimeout, setTimeout(() => {
582
- this.lastVelocity = this.velocity;
583
- this.velocity = 0;
584
- this.isScrolling = false;
585
- this.emit();
586
- }, 400), "f");
587
- }
588
- }
589
- };
590
- // Set version
591
- window.lenisVersion = version;
592
- // Check if wrapper is html or body, fallback to window
593
- if (!wrapper ||
594
- wrapper === document.documentElement ||
595
- wrapper === document.body) {
596
- wrapper = window;
597
- }
598
- // Setup options
599
- this.options = {
600
- wrapper,
601
- content,
602
- eventsTarget,
603
- smoothWheel,
604
- syncTouch,
605
- syncTouchLerp,
606
- touchInertiaMultiplier,
607
- duration,
608
- easing,
609
- lerp,
610
- infinite,
611
- gestureOrientation,
612
- orientation,
613
- touchMultiplier,
614
- wheelMultiplier,
615
- autoResize,
616
- prevent,
617
- virtualScroll,
618
- __experimental__naiveDimensions,
619
- };
620
- // Setup dimensions instance
621
- this.dimensions = new Dimensions(wrapper, content, { autoResize });
622
- // Setup class name
623
- this.updateClassName();
624
- // Set the initial scroll value for all scroll information
625
- this.targetScroll = this.animatedScroll = this.actualScroll;
626
- // Add event listeners
627
- this.options.wrapper.addEventListener('scroll', this.onNativeScroll, false);
628
- this.options.wrapper.addEventListener('pointerdown', this.onPointerDown, false);
629
- // Setup virtual scroll instance
630
- this.virtualScroll = new VirtualScroll(eventsTarget, {
631
- touchMultiplier,
632
- wheelMultiplier,
633
- });
634
- this.virtualScroll.on('scroll', this.onVirtualScroll);
635
- }
636
- /**
637
- * Destroy the lenis instance, remove all event listeners and clean up the class name
638
- */
639
- destroy() {
640
- this.emitter.destroy();
641
- this.options.wrapper.removeEventListener('scroll', this.onNativeScroll, false);
642
- this.options.wrapper.removeEventListener('pointerdown', this.onPointerDown, false);
643
- this.virtualScroll.destroy();
644
- this.dimensions.destroy();
645
- this.cleanUpClassName();
646
- }
647
- on(event, callback) {
648
- return this.emitter.on(event, callback);
649
- }
650
- off(event, callback) {
651
- return this.emitter.off(event, callback);
652
- }
653
- setScroll(scroll) {
654
- // apply scroll value immediately
655
- if (this.isHorizontal) {
656
- this.rootElement.scrollLeft = scroll;
657
- }
658
- else {
659
- this.rootElement.scrollTop = scroll;
660
- }
661
- }
662
- /**
663
- * Force lenis to recalculate the dimensions
664
- */
665
- resize() {
666
- this.dimensions.resize();
667
- this.animatedScroll = this.targetScroll = this.actualScroll;
668
- this.emit();
669
- }
670
- emit() {
671
- this.emitter.emit('scroll', this);
672
- }
673
- reset() {
674
- this.isLocked = false;
675
- this.isScrolling = false;
676
- this.animatedScroll = this.targetScroll = this.actualScroll;
677
- this.lastVelocity = this.velocity = 0;
678
- this.animate.stop();
679
- }
680
- /**
681
- * Start lenis scroll after it has been stopped
682
- */
683
- start() {
684
- if (!this.isStopped)
685
- return;
686
- this.isStopped = false;
687
- this.reset();
688
- }
689
- /**
690
- * Stop lenis scroll
691
- */
692
- stop() {
693
- if (this.isStopped)
694
- return;
695
- this.isStopped = true;
696
- this.animate.stop();
697
- this.reset();
698
- }
699
- /**
700
- * RequestAnimationFrame for lenis
701
- *
702
- * @param time The time in ms from an external clock like `requestAnimationFrame` or Tempus
703
- */
704
- raf(time) {
705
- const deltaTime = time - (this.time || time);
706
- this.time = time;
707
- this.animate.advance(deltaTime * 0.001);
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, duration = this.options.duration, easing = this.options.easing, lerp = this.options.lerp, onStart, onComplete, force = false, // scroll even if stopped
730
- programmatic = true, // called from outside of the class
731
- userData, } = {}) {
732
- if ((this.isStopped || this.isLocked) && !force)
733
- return;
734
- // keywords
735
- if (typeof target === 'string' &&
736
- ['top', 'left', 'start'].includes(target)) {
737
- target = 0;
738
- }
739
- else if (typeof target === 'string' &&
740
- ['bottom', 'right', 'end'].includes(target)) {
741
- target = this.limit;
742
- }
743
- else {
744
- let node;
745
- if (typeof target === 'string') {
746
- // CSS selector
747
- node = document.querySelector(target);
748
- }
749
- else if (target instanceof HTMLElement && (target === null || target === void 0 ? void 0 : target.nodeType)) {
750
- // Node element
751
- node = target;
752
- }
753
- if (node) {
754
- if (this.options.wrapper !== window) {
755
- // nested scroll offset correction
756
- const wrapperRect = this.rootElement.getBoundingClientRect();
757
- offset -= this.isHorizontal ? wrapperRect.left : wrapperRect.top;
758
- }
759
- const rect = node.getBoundingClientRect();
760
- target =
761
- (this.isHorizontal ? rect.left : rect.top) + this.animatedScroll;
762
- }
763
- }
764
- if (typeof target !== 'number')
765
- return;
766
- target += offset;
767
- target = Math.round(target);
768
- if (this.options.infinite) {
769
- if (programmatic) {
770
- this.targetScroll = this.animatedScroll = this.scroll;
771
- }
772
- }
773
- else {
774
- target = clamp(0, target, this.limit);
775
- }
776
- if (target === this.targetScroll) {
777
- onStart === null || onStart === void 0 ? void 0 : onStart(this);
778
- onComplete === null || onComplete === void 0 ? void 0 : onComplete(this);
779
- return;
780
- }
781
- this.userData = userData !== null && userData !== void 0 ? userData : {};
782
- if (immediate) {
783
- this.animatedScroll = this.targetScroll = target;
784
- this.setScroll(this.scroll);
785
- this.reset();
786
- this.preventNextNativeScrollEvent();
787
- this.emit();
788
- onComplete === null || onComplete === void 0 ? void 0 : onComplete(this);
789
- this.userData = {};
790
- return;
791
- }
792
- if (!programmatic) {
793
- this.targetScroll = target;
794
- }
795
- this.animate.fromTo(this.animatedScroll, target, {
796
- duration,
797
- easing,
798
- lerp,
799
- onStart: () => {
800
- // started
801
- if (lock)
802
- this.isLocked = true;
803
- this.isScrolling = 'smooth';
804
- onStart === null || onStart === void 0 ? void 0 : onStart(this);
805
- },
806
- onUpdate: (value, completed) => {
807
- this.isScrolling = 'smooth';
808
- // updated
809
- this.lastVelocity = this.velocity;
810
- this.velocity = value - this.animatedScroll;
811
- this.direction = Math.sign(this.velocity);
812
- this.animatedScroll = value;
813
- this.setScroll(this.scroll);
814
- if (programmatic) {
815
- // wheel during programmatic should stop it
816
- this.targetScroll = value;
817
- }
818
- if (!completed)
819
- this.emit();
820
- if (completed) {
821
- this.reset();
822
- this.emit();
823
- onComplete === null || onComplete === void 0 ? void 0 : onComplete(this);
824
- this.userData = {};
825
- // avoid emitting event twice
826
- this.preventNextNativeScrollEvent();
827
- }
828
- },
829
- });
830
- }
831
- preventNextNativeScrollEvent() {
832
- __classPrivateFieldSet(this, _Lenis_preventNextNativeScrollEvent, true, "f");
833
- requestAnimationFrame(() => {
834
- __classPrivateFieldSet(this, _Lenis_preventNextNativeScrollEvent, false, "f");
835
- });
836
- }
837
- /**
838
- * The root element on which lenis is instanced
839
- */
840
- get rootElement() {
841
- return (this.options.wrapper === window
842
- ? document.documentElement
843
- : this.options.wrapper);
844
- }
845
- /**
846
- * The limit which is the maximum scroll value
847
- */
848
- get limit() {
849
- if (this.options.__experimental__naiveDimensions) {
850
- if (this.isHorizontal) {
851
- return this.rootElement.scrollWidth - this.rootElement.clientWidth;
852
- }
853
- else {
854
- return this.rootElement.scrollHeight - this.rootElement.clientHeight;
855
- }
856
- }
857
- else {
858
- return this.dimensions.limit[this.isHorizontal ? 'x' : 'y'];
859
- }
860
- }
861
- /**
862
- * Whether or not the scroll is horizontal
863
- */
864
- get isHorizontal() {
865
- return this.options.orientation === 'horizontal';
866
- }
867
- /**
868
- * The actual scroll value
869
- */
870
- get actualScroll() {
871
- // value browser takes into account
872
- return this.isHorizontal
873
- ? this.rootElement.scrollLeft
874
- : this.rootElement.scrollTop;
875
- }
876
- /**
877
- * The current scroll value
878
- */
879
- get scroll() {
880
- return this.options.infinite
881
- ? modulo(this.animatedScroll, this.limit)
882
- : this.animatedScroll;
883
- }
884
- /**
885
- * The progress of the scroll relative to the limit
886
- */
887
- get progress() {
888
- // avoid progress to be NaN
889
- return this.limit === 0 ? 1 : this.scroll / this.limit;
890
- }
891
- /**
892
- * Current scroll state
893
- */
894
- get isScrolling() {
895
- return __classPrivateFieldGet(this, _Lenis_isScrolling, "f");
896
- }
897
- set isScrolling(value) {
898
- if (__classPrivateFieldGet(this, _Lenis_isScrolling, "f") !== value) {
899
- __classPrivateFieldSet(this, _Lenis_isScrolling, value, "f");
900
- this.updateClassName();
901
- }
902
- }
903
- /**
904
- * Check if lenis is stopped
905
- */
906
- get isStopped() {
907
- return __classPrivateFieldGet(this, _Lenis_isStopped, "f");
908
- }
909
- set isStopped(value) {
910
- if (__classPrivateFieldGet(this, _Lenis_isStopped, "f") !== value) {
911
- __classPrivateFieldSet(this, _Lenis_isStopped, value, "f");
912
- this.updateClassName();
913
- }
914
- }
915
- /**
916
- * Check if lenis is locked
917
- */
918
- get isLocked() {
919
- return __classPrivateFieldGet(this, _Lenis_isLocked, "f");
920
- }
921
- set isLocked(value) {
922
- if (__classPrivateFieldGet(this, _Lenis_isLocked, "f") !== value) {
923
- __classPrivateFieldSet(this, _Lenis_isLocked, value, "f");
924
- this.updateClassName();
925
- }
926
- }
927
- /**
928
- * Check if lenis is smooth scrolling
929
- */
930
- get isSmooth() {
931
- return this.isScrolling === 'smooth';
932
- }
933
- /**
934
- * The class name applied to the wrapper element
935
- */
936
- get className() {
937
- let className = 'lenis';
938
- if (this.isStopped)
939
- className += ' lenis-stopped';
940
- if (this.isLocked)
941
- className += ' lenis-locked';
942
- if (this.isScrolling)
943
- className += ' lenis-scrolling';
944
- if (this.isScrolling === 'smooth')
945
- className += ' lenis-smooth';
946
- return className;
947
- }
948
- updateClassName() {
949
- this.cleanUpClassName();
950
- this.rootElement.className =
951
- `${this.rootElement.className} ${this.className}`.trim();
952
- }
953
- cleanUpClassName() {
954
- this.rootElement.className = this.rootElement.className
955
- .replace(/lenis(-\w+)?/g, '')
956
- .trim();
957
- }
958
- }
959
- _Lenis_isScrolling = new WeakMap(), _Lenis_isStopped = new WeakMap(), _Lenis_isLocked = new WeakMap(), _Lenis_preventNextNativeScrollEvent = new WeakMap(), _Lenis_resetVelocityTimeout = new WeakMap();
960
-
961
- return Lenis;
920
+ return Lenis;
962
921
 
963
922
  }));
964
923
  //# sourceMappingURL=lenis.js.map