lenis 1.1.13 → 1.1.14-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,342 +1,308 @@
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';
1
+ // packages/snap/src/debounce.ts
2
+ function debounce(callback, delay) {
3
+ let timer;
4
+ return function(...args) {
5
+ let context = this;
6
+ clearTimeout(timer);
7
+ timer = setTimeout(() => {
8
+ timer = void 0;
9
+ callback.apply(context, args);
10
+ }, delay);
11
+ };
12
+ }
6
13
 
7
- function debounce(callback, delay) {
8
- let timer;
9
- return function (...args) {
10
- let context = this;
11
- clearTimeout(timer);
12
- timer = setTimeout(() => {
13
- timer = undefined;
14
- callback.apply(context, args);
15
- }, delay);
16
- };
14
+ // packages/snap/src/element.ts
15
+ function removeParentSticky(element) {
16
+ const position = getComputedStyle(element).position;
17
+ const isSticky = position === "sticky";
18
+ if (isSticky) {
19
+ element.style.setProperty("position", "static");
20
+ element.dataset.sticky = "true";
21
+ }
22
+ if (element.offsetParent) {
23
+ removeParentSticky(element.offsetParent);
24
+ }
25
+ }
26
+ function addParentSticky(element) {
27
+ if (element?.dataset?.sticky === "true") {
28
+ element.style.removeProperty("position");
29
+ delete element.dataset.sticky;
30
+ }
31
+ if (element.offsetParent) {
32
+ addParentSticky(element.offsetParent);
33
+ }
34
+ }
35
+ function offsetTop(element, accumulator = 0) {
36
+ const top = accumulator + element.offsetTop;
37
+ if (element.offsetParent) {
38
+ return offsetTop(element.offsetParent, top);
39
+ }
40
+ return top;
41
+ }
42
+ function offsetLeft(element, accumulator = 0) {
43
+ const left = accumulator + element.offsetLeft;
44
+ if (element.offsetParent) {
45
+ return offsetLeft(element.offsetParent, left);
46
+ }
47
+ return left;
48
+ }
49
+ function scrollTop(element, accumulator = 0) {
50
+ const top = accumulator + element.scrollTop;
51
+ if (element.offsetParent) {
52
+ return scrollTop(element.offsetParent, top);
53
+ }
54
+ return top + window.scrollY;
55
+ }
56
+ function scrollLeft(element, accumulator = 0) {
57
+ const left = accumulator + element.scrollLeft;
58
+ if (element.offsetParent) {
59
+ return scrollLeft(element.offsetParent, left);
60
+ }
61
+ return left + window.scrollX;
62
+ }
63
+ var SnapElement = class {
64
+ element;
65
+ options;
66
+ align;
67
+ // @ts-ignore
68
+ rect = {};
69
+ wrapperResizeObserver;
70
+ resizeObserver;
71
+ constructor(element, {
72
+ align = ["start"],
73
+ ignoreSticky = true,
74
+ ignoreTransform = false
75
+ } = {}) {
76
+ this.element = element;
77
+ this.options = { align, ignoreSticky, ignoreTransform };
78
+ this.align = [align].flat();
79
+ this.wrapperResizeObserver = new ResizeObserver(this.onWrapperResize);
80
+ this.wrapperResizeObserver.observe(document.body);
81
+ this.onWrapperResize();
82
+ this.resizeObserver = new ResizeObserver(this.onResize);
83
+ this.resizeObserver.observe(this.element);
84
+ this.setRect({
85
+ width: this.element.offsetWidth,
86
+ height: this.element.offsetHeight
87
+ });
88
+ }
89
+ destroy() {
90
+ this.wrapperResizeObserver.disconnect();
91
+ this.resizeObserver.disconnect();
92
+ }
93
+ setRect({
94
+ top,
95
+ left,
96
+ width,
97
+ height,
98
+ element
99
+ } = {}) {
100
+ top = top ?? this.rect.top;
101
+ left = left ?? this.rect.left;
102
+ width = width ?? this.rect.width;
103
+ height = height ?? this.rect.height;
104
+ element = element ?? this.rect.element;
105
+ if (top === this.rect.top && left === this.rect.left && width === this.rect.width && height === this.rect.height && element === this.rect.element)
106
+ return;
107
+ this.rect.top = top;
108
+ this.rect.y = top;
109
+ this.rect.width = width;
110
+ this.rect.height = height;
111
+ this.rect.left = left;
112
+ this.rect.x = left;
113
+ this.rect.bottom = top + height;
114
+ this.rect.right = left + width;
115
+ }
116
+ onWrapperResize = () => {
117
+ let top, left;
118
+ if (this.options.ignoreSticky) removeParentSticky(this.element);
119
+ if (this.options.ignoreTransform) {
120
+ top = offsetTop(this.element);
121
+ left = offsetLeft(this.element);
122
+ } else {
123
+ const rect = this.element.getBoundingClientRect();
124
+ top = rect.top + scrollTop(this.element);
125
+ left = rect.left + scrollLeft(this.element);
17
126
  }
127
+ if (this.options.ignoreSticky) addParentSticky(this.element);
128
+ this.setRect({ top, left });
129
+ };
130
+ onResize = ([entry]) => {
131
+ if (!entry?.borderBoxSize[0]) return;
132
+ const width = entry.borderBoxSize[0].inlineSize;
133
+ const height = entry.borderBoxSize[0].blockSize;
134
+ this.setRect({ width, height });
135
+ };
136
+ };
18
137
 
19
- function removeParentSticky(element) {
20
- const position = getComputedStyle(element).position;
21
- const isSticky = position === 'sticky';
22
- if (isSticky) {
23
- element.style.setProperty('position', 'static');
24
- element.dataset.sticky = 'true';
25
- }
26
- if (element.offsetParent) {
27
- removeParentSticky(element.offsetParent);
28
- }
29
- }
30
- function addParentSticky(element) {
31
- var _a;
32
- if (((_a = element === null || element === void 0 ? void 0 : element.dataset) === null || _a === void 0 ? void 0 : _a.sticky) === 'true') {
33
- element.style.removeProperty('position');
34
- delete element.dataset.sticky;
35
- }
36
- if (element.offsetParent) {
37
- addParentSticky(element.offsetParent);
38
- }
39
- }
40
- function offsetTop(element, accumulator = 0) {
41
- const top = accumulator + element.offsetTop;
42
- if (element.offsetParent) {
43
- return offsetTop(element.offsetParent, top);
44
- }
45
- return top;
46
- }
47
- function offsetLeft(element, accumulator = 0) {
48
- const left = accumulator + element.offsetLeft;
49
- if (element.offsetParent) {
50
- return offsetLeft(element.offsetParent, left);
51
- }
52
- return left;
53
- }
54
- function scrollTop(element, accumulator = 0) {
55
- const top = accumulator + element.scrollTop;
56
- if (element.offsetParent) {
57
- return scrollTop(element.offsetParent, top);
58
- }
59
- return top + window.scrollY;
60
- }
61
- function scrollLeft(element, accumulator = 0) {
62
- const left = accumulator + element.scrollLeft;
63
- if (element.offsetParent) {
64
- return scrollLeft(element.offsetParent, left);
65
- }
66
- return left + window.scrollX;
67
- }
68
- class SnapElement {
69
- constructor(element, { align = ['start'], ignoreSticky = true, ignoreTransform = false, } = {}) {
70
- // @ts-ignore
71
- this.rect = {};
72
- this.onWrapperResize = () => {
73
- let top, left;
74
- if (this.options.ignoreSticky)
75
- removeParentSticky(this.element);
76
- if (this.options.ignoreTransform) {
77
- top = offsetTop(this.element);
78
- left = offsetLeft(this.element);
79
- }
80
- else {
81
- const rect = this.element.getBoundingClientRect();
82
- top = rect.top + scrollTop(this.element);
83
- left = rect.left + scrollLeft(this.element);
84
- }
85
- if (this.options.ignoreSticky)
86
- addParentSticky(this.element);
87
- this.setRect({ top, left });
88
- };
89
- this.onResize = ([entry]) => {
90
- const width = entry.borderBoxSize[0].inlineSize;
91
- const height = entry.borderBoxSize[0].blockSize;
92
- this.setRect({ width, height });
93
- };
94
- this.element = element;
95
- this.options = { align, ignoreSticky, ignoreTransform };
96
- // this.ignoreSticky = ignoreSticky
97
- // this.ignoreTransform = ignoreTransform
98
- this.align = [align].flat();
99
- // TODO: assing rect immediately
100
- this.wrapperResizeObserver = new ResizeObserver(this.onWrapperResize);
101
- this.wrapperResizeObserver.observe(document.body);
102
- this.onWrapperResize();
103
- this.resizeObserver = new ResizeObserver(this.onResize);
104
- this.resizeObserver.observe(this.element);
105
- this.setRect({
106
- width: this.element.offsetWidth,
107
- height: this.element.offsetHeight,
108
- });
109
- }
110
- destroy() {
111
- this.wrapperResizeObserver.disconnect();
112
- this.resizeObserver.disconnect();
113
- }
114
- setRect({ top, left, width, height, element, } = {}) {
115
- top = top !== null && top !== void 0 ? top : this.rect.top;
116
- left = left !== null && left !== void 0 ? left : this.rect.left;
117
- width = width !== null && width !== void 0 ? width : this.rect.width;
118
- height = height !== null && height !== void 0 ? height : this.rect.height;
119
- element = element !== null && element !== void 0 ? element : this.rect.element;
120
- if (top === this.rect.top &&
121
- left === this.rect.left &&
122
- width === this.rect.width &&
123
- height === this.rect.height &&
124
- element === this.rect.element)
125
- return;
126
- this.rect.top = top;
127
- this.rect.y = top;
128
- this.rect.width = width;
129
- this.rect.height = height;
130
- this.rect.left = left;
131
- this.rect.x = left;
132
- this.rect.bottom = top + height;
133
- this.rect.right = left + width;
134
- }
135
- }
138
+ // packages/snap/src/uid.ts
139
+ var index = 0;
140
+ function uid() {
141
+ return index++;
142
+ }
136
143
 
137
- let index = 0;
138
- function uid() {
139
- return index++;
144
+ // packages/snap/src/snap.ts
145
+ var Snap = class {
146
+ constructor(lenis, {
147
+ type = "mandatory",
148
+ lerp,
149
+ easing,
150
+ duration,
151
+ velocityThreshold = 1,
152
+ debounce: debounceDelay = 0,
153
+ onSnapStart,
154
+ onSnapComplete
155
+ } = {}) {
156
+ this.lenis = lenis;
157
+ this.options = {
158
+ type,
159
+ lerp,
160
+ easing,
161
+ duration,
162
+ velocityThreshold,
163
+ debounce: debounceDelay,
164
+ onSnapStart,
165
+ onSnapComplete
166
+ };
167
+ this.onWindowResize();
168
+ window.addEventListener("resize", this.onWindowResize, false);
169
+ this.onSnapDebounced = debounce(this.onSnap, this.options.debounce);
170
+ this.lenis.on("scroll", this.onScroll);
171
+ }
172
+ options;
173
+ elements = /* @__PURE__ */ new Map();
174
+ snaps = /* @__PURE__ */ new Map();
175
+ viewport = {
176
+ width: window.innerWidth,
177
+ height: window.innerHeight
178
+ };
179
+ isStopped = false;
180
+ onSnapDebounced;
181
+ /**
182
+ * Destroy the snap instance
183
+ */
184
+ destroy() {
185
+ this.lenis.off("scroll", this.onScroll);
186
+ window.removeEventListener("resize", this.onWindowResize, false);
187
+ this.elements.forEach((element) => element.destroy());
188
+ }
189
+ /**
190
+ * Start the snap after it has been stopped
191
+ */
192
+ start() {
193
+ this.isStopped = false;
194
+ }
195
+ /**
196
+ * Stop the snap
197
+ */
198
+ stop() {
199
+ this.isStopped = true;
200
+ }
201
+ /**
202
+ * Add a snap to the snap instance
203
+ *
204
+ * @param value The value to snap to
205
+ * @param userData User data that will be forwarded through the snap event
206
+ * @returns Unsubscribe function
207
+ */
208
+ add(value, userData = {}) {
209
+ const id = uid();
210
+ this.snaps.set(id, { value, userData });
211
+ return () => this.remove(id);
212
+ }
213
+ /**
214
+ * Remove a snap from the snap instance
215
+ *
216
+ * @param id The snap id of the snap to remove
217
+ */
218
+ remove(id) {
219
+ this.snaps.delete(id);
220
+ }
221
+ /**
222
+ * Add an element to the snap instance
223
+ *
224
+ * @param element The element to add
225
+ * @param options The options for the element
226
+ * @returns Unsubscribe function
227
+ */
228
+ addElement(element, options = {}) {
229
+ const id = uid();
230
+ this.elements.set(id, new SnapElement(element, options));
231
+ return () => this.removeElement(id);
232
+ }
233
+ /**
234
+ * Remove an element from the snap instance
235
+ *
236
+ * @param id The snap id of the snap element to remove
237
+ */
238
+ removeElement(id) {
239
+ this.elements.delete(id);
240
+ }
241
+ onWindowResize = () => {
242
+ this.viewport.width = window.innerWidth;
243
+ this.viewport.height = window.innerHeight;
244
+ };
245
+ onScroll = ({
246
+ // scroll,
247
+ // limit,
248
+ lastVelocity,
249
+ velocity,
250
+ // isScrolling,
251
+ userData
252
+ }) => {
253
+ if (this.isStopped) return;
254
+ const isDecelerating = Math.abs(lastVelocity) > Math.abs(velocity);
255
+ const isTurningBack = Math.sign(lastVelocity) !== Math.sign(velocity) && velocity !== 0;
256
+ if (Math.abs(velocity) < this.options.velocityThreshold && // !isTouching &&
257
+ isDecelerating && !isTurningBack && userData?.initiator !== "snap") {
258
+ this.onSnapDebounced();
140
259
  }
141
-
142
- /**
143
- * Snap class to handle the snap functionality
144
- *
145
- * @example
146
- * const snap = new Snap(lenis, {
147
- * type: 'mandatory', // 'mandatory', 'proximity'
148
- * lerp: 0.1,
149
- * duration: 1,
150
- * easing: (t) => t,
151
- * onSnapStart: (snap) => {
152
- * console.log('onSnapStart', snap)
153
- * },
154
- * onSnapComplete: (snap) => {
155
- * console.log('onSnapComplete', snap)
156
- * },
157
- * })
158
- *
159
- * snap.add(500) // snap at 500px
160
- *
161
- * const removeSnap = snap.add(500)
162
- *
163
- * if (someCondition) {
164
- * removeSnap()
165
- * }
166
- */
167
- class Snap {
168
- constructor(lenis, { type = 'mandatory', lerp, easing, duration, velocityThreshold = 1, debounce: debounceDelay = 0, onSnapStart, onSnapComplete, } = {}) {
169
- this.lenis = lenis;
170
- this.elements = new Map();
171
- this.snaps = new Map();
172
- this.viewport = {
173
- width: window.innerWidth,
174
- height: window.innerHeight,
175
- };
176
- this.isStopped = false;
177
- this.onWindowResize = () => {
178
- this.viewport.width = window.innerWidth;
179
- this.viewport.height = window.innerHeight;
180
- };
181
- this.onScroll = ({
182
- // scroll,
183
- // limit,
184
- lastVelocity, velocity,
185
- // isScrolling,
186
- userData, }) => {
187
- if (this.isStopped)
188
- return;
189
- // return
190
- const isDecelerating = Math.abs(lastVelocity) > Math.abs(velocity);
191
- const isTurningBack = Math.sign(lastVelocity) !== Math.sign(velocity) && velocity !== 0;
192
- if (Math.abs(velocity) < this.options.velocityThreshold &&
193
- // !isTouching &&
194
- isDecelerating &&
195
- !isTurningBack &&
196
- (userData === null || userData === void 0 ? void 0 : userData.initiator) !== 'snap') {
197
- this.onSnapDebounced();
198
- }
199
- };
200
- this.onSnap = () => {
201
- let { scroll, isHorizontal } = this.lenis;
202
- scroll = Math.ceil(this.lenis.scroll);
203
- let snaps = [...this.snaps.values()];
204
- this.elements.forEach(({ rect, align }) => {
205
- let value;
206
- align.forEach((align) => {
207
- if (align === 'start') {
208
- value = rect.top;
209
- }
210
- else if (align === 'center') {
211
- value = isHorizontal
212
- ? rect.left + rect.width / 2 - this.viewport.width / 2
213
- : rect.top + rect.height / 2 - this.viewport.height / 2;
214
- }
215
- else if (align === 'end') {
216
- value = isHorizontal
217
- ? rect.left + rect.width - this.viewport.width
218
- : rect.top + rect.height - this.viewport.height;
219
- }
220
- if (typeof value === 'number') {
221
- snaps.push({ value: Math.ceil(value), userData: {} });
222
- }
223
- });
224
- });
225
- snaps = snaps.sort((a, b) => Math.abs(a.value) - Math.abs(b.value));
226
- let prevSnap = snaps.findLast(({ value }) => value <= scroll);
227
- if (prevSnap === undefined)
228
- prevSnap = snaps[0];
229
- const distanceToPrevSnap = Math.abs(scroll - prevSnap.value);
230
- let nextSnap = snaps.find(({ value }) => value >= scroll);
231
- if (nextSnap === undefined)
232
- nextSnap = snaps[snaps.length - 1];
233
- const distanceToNextSnap = Math.abs(scroll - nextSnap.value);
234
- const snap = distanceToPrevSnap < distanceToNextSnap ? prevSnap : nextSnap;
235
- const distance = Math.abs(scroll - snap.value);
236
- if (this.options.type === 'mandatory' ||
237
- (this.options.type === 'proximity' &&
238
- distance <=
239
- (isHorizontal
240
- ? this.lenis.dimensions.width
241
- : this.lenis.dimensions.height))) {
242
- // this.__isScrolling = true
243
- // this.onSnapStart?.(snap)
244
- // console.log('scroll to')
245
- this.lenis.scrollTo(snap.value, {
246
- lerp: this.options.lerp,
247
- easing: this.options.easing,
248
- duration: this.options.duration,
249
- userData: { initiator: 'snap' },
250
- onStart: () => {
251
- var _a, _b;
252
- (_b = (_a = this.options).onSnapStart) === null || _b === void 0 ? void 0 : _b.call(_a, snap);
253
- },
254
- onComplete: () => {
255
- var _a, _b;
256
- (_b = (_a = this.options).onSnapComplete) === null || _b === void 0 ? void 0 : _b.call(_a, snap);
257
- },
258
- });
259
- }
260
- // console.timeEnd('scroll')
261
- };
262
- this.options = {
263
- type,
264
- lerp,
265
- easing,
266
- duration,
267
- velocityThreshold,
268
- debounce: debounceDelay,
269
- onSnapStart,
270
- onSnapComplete,
271
- };
272
- this.onWindowResize();
273
- window.addEventListener('resize', this.onWindowResize, false);
274
- this.onSnapDebounced = debounce(this.onSnap, this.options.debounce);
275
- this.lenis.on('scroll', this.onScroll);
260
+ };
261
+ onSnap = () => {
262
+ let { scroll, isHorizontal } = this.lenis;
263
+ scroll = Math.ceil(this.lenis.scroll);
264
+ let snaps = [...this.snaps.values()];
265
+ this.elements.forEach(({ rect, align }) => {
266
+ let value;
267
+ align.forEach((align2) => {
268
+ if (align2 === "start") {
269
+ value = rect.top;
270
+ } else if (align2 === "center") {
271
+ value = isHorizontal ? rect.left + rect.width / 2 - this.viewport.width / 2 : rect.top + rect.height / 2 - this.viewport.height / 2;
272
+ } else if (align2 === "end") {
273
+ value = isHorizontal ? rect.left + rect.width - this.viewport.width : rect.top + rect.height - this.viewport.height;
276
274
  }
277
- /**
278
- * Destroy the snap instance
279
- */
280
- destroy() {
281
- this.lenis.off('scroll', this.onScroll);
282
- window.removeEventListener('resize', this.onWindowResize, false);
283
- this.elements.forEach((element) => element.destroy());
275
+ if (typeof value === "number") {
276
+ snaps.push({ value: Math.ceil(value), userData: {} });
284
277
  }
285
- /**
286
- * Start the snap after it has been stopped
287
- */
288
- start() {
289
- this.isStopped = false;
290
- }
291
- /**
292
- * Stop the snap
293
- */
294
- stop() {
295
- this.isStopped = true;
296
- }
297
- /**
298
- * Add a snap to the snap instance
299
- *
300
- * @param value The value to snap to
301
- * @param userData User data that will be forwarded through the snap event
302
- * @returns Unsubscribe function
303
- */
304
- add(value, userData = {}) {
305
- const id = uid();
306
- this.snaps.set(id, { value, userData });
307
- return () => this.remove(id);
308
- }
309
- /**
310
- * Remove a snap from the snap instance
311
- *
312
- * @param id The snap id of the snap to remove
313
- */
314
- remove(id) {
315
- this.snaps.delete(id);
316
- }
317
- /**
318
- * Add an element to the snap instance
319
- *
320
- * @param element The element to add
321
- * @param options The options for the element
322
- * @returns Unsubscribe function
323
- */
324
- addElement(element, options = {}) {
325
- const id = uid();
326
- this.elements.set(id, new SnapElement(element, options));
327
- return () => this.removeElement(id);
328
- }
329
- /**
330
- * Remove an element from the snap instance
331
- *
332
- * @param id The snap id of the snap element to remove
333
- */
334
- removeElement(id) {
335
- this.elements.delete(id);
278
+ });
279
+ });
280
+ snaps = snaps.sort((a, b) => Math.abs(a.value) - Math.abs(b.value));
281
+ let prevSnap = snaps.findLast(({ value }) => value <= scroll);
282
+ if (prevSnap === void 0) prevSnap = snaps[0];
283
+ const distanceToPrevSnap = Math.abs(scroll - prevSnap.value);
284
+ let nextSnap = snaps.find(({ value }) => value >= scroll);
285
+ if (nextSnap === void 0) nextSnap = snaps[snaps.length - 1];
286
+ const distanceToNextSnap = Math.abs(scroll - nextSnap.value);
287
+ const snap = distanceToPrevSnap < distanceToNextSnap ? prevSnap : nextSnap;
288
+ const distance = Math.abs(scroll - snap.value);
289
+ if (this.options.type === "mandatory" || this.options.type === "proximity" && distance <= (isHorizontal ? this.lenis.dimensions.width : this.lenis.dimensions.height)) {
290
+ this.lenis.scrollTo(snap.value, {
291
+ lerp: this.options.lerp,
292
+ easing: this.options.easing,
293
+ duration: this.options.duration,
294
+ userData: { initiator: "snap" },
295
+ onStart: () => {
296
+ this.options.onSnapStart?.(snap);
297
+ },
298
+ onComplete: () => {
299
+ this.options.onSnapComplete?.(snap);
336
300
  }
301
+ });
337
302
  }
303
+ };
304
+ };
338
305
 
339
- return Snap;
340
-
341
- }));
342
- //# sourceMappingURL=lenis-snap.js.map
306
+ // packages/snap/browser.ts
307
+ globalThis.Snap = Snap;
308
+ //# sourceMappingURL=lenis-snap.js.map