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