lenis 1.1.13 → 1.1.14-dev.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lenis.js DELETED
@@ -1,923 +0,0 @@
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());
5
- })(this, (function () { 'use strict';
6
-
7
- var version = "1.1.13";
8
-
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
- }
55
-
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
- }
131
-
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
- }
143
-
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
- }
214
-
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
- }
272
-
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
- }
384
-
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
- }
919
-
920
- return Lenis;
921
-
922
- }));
923
- //# sourceMappingURL=lenis.js.map