lenis 1.3.11 → 1.3.12-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lenis-snap.d.ts +7 -5
- package/dist/lenis-snap.js +49 -34
- package/dist/lenis-snap.js.map +1 -1
- package/dist/lenis-snap.min.js +1 -1
- package/dist/lenis-snap.min.js.map +1 -1
- package/dist/lenis-snap.mjs +49 -34
- package/dist/lenis-snap.mjs.map +1 -1
- package/dist/lenis.js +1 -1
- package/dist/lenis.js.map +1 -1
- package/dist/lenis.min.js +1 -1
- package/dist/lenis.min.js.map +1 -1
- package/dist/lenis.mjs +1 -1
- package/dist/lenis.mjs.map +1 -1
- package/package.json +1 -1
package/dist/lenis-snap.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Lenis, {
|
|
1
|
+
import Lenis, { EasingFunction } from 'lenis';
|
|
2
2
|
|
|
3
3
|
type SnapElementOptions = {
|
|
4
4
|
align?: string | string[];
|
|
@@ -38,7 +38,6 @@ declare class SnapElement {
|
|
|
38
38
|
|
|
39
39
|
type SnapItem = {
|
|
40
40
|
value: number;
|
|
41
|
-
userData: UserData;
|
|
42
41
|
};
|
|
43
42
|
type OnSnapCallback = (item: SnapItem) => void;
|
|
44
43
|
type SnapOptions = {
|
|
@@ -46,7 +45,7 @@ type SnapOptions = {
|
|
|
46
45
|
* Snap type
|
|
47
46
|
* @default mandatory
|
|
48
47
|
*/
|
|
49
|
-
type?: 'mandatory' | 'proximity';
|
|
48
|
+
type?: 'mandatory' | 'proximity' | 'slide';
|
|
50
49
|
/**
|
|
51
50
|
* Linear interpolation (lerp) intensity (between 0 and 1)
|
|
52
51
|
*/
|
|
@@ -113,7 +112,8 @@ declare class Snap {
|
|
|
113
112
|
height: number;
|
|
114
113
|
};
|
|
115
114
|
isStopped: boolean;
|
|
116
|
-
onSnapDebounced
|
|
115
|
+
onSnapDebounced?: () => void;
|
|
116
|
+
currentSnapIndex: number;
|
|
117
117
|
constructor(lenis: Lenis, { type, lerp, easing, duration, distanceThreshold, debounce: debounceDelay, onSnapStart, onSnapComplete, }?: SnapOptions);
|
|
118
118
|
/**
|
|
119
119
|
* Destroy the snap instance
|
|
@@ -134,7 +134,7 @@ declare class Snap {
|
|
|
134
134
|
* @param userData User data that will be forwarded through the snap event
|
|
135
135
|
* @returns Unsubscribe function
|
|
136
136
|
*/
|
|
137
|
-
add(value: number
|
|
137
|
+
add(value: number): () => boolean;
|
|
138
138
|
/**
|
|
139
139
|
* Add an element to the snap instance
|
|
140
140
|
*
|
|
@@ -144,6 +144,8 @@ declare class Snap {
|
|
|
144
144
|
*/
|
|
145
145
|
addElement(element: HTMLElement, options?: SnapElementOptions): () => boolean;
|
|
146
146
|
private onWindowResize;
|
|
147
|
+
private computeSnaps;
|
|
148
|
+
private onSlide;
|
|
147
149
|
private onSnap;
|
|
148
150
|
}
|
|
149
151
|
|
package/dist/lenis-snap.js
CHANGED
|
@@ -143,13 +143,13 @@ function uid() {
|
|
|
143
143
|
|
|
144
144
|
// packages/snap/src/snap.ts
|
|
145
145
|
var Snap = class {
|
|
146
|
+
// used for slide type
|
|
146
147
|
constructor(lenis, {
|
|
147
148
|
type = "proximity",
|
|
148
149
|
lerp,
|
|
149
150
|
easing,
|
|
150
151
|
duration,
|
|
151
152
|
distanceThreshold = "50%",
|
|
152
|
-
// velocityThreshold = 1.2,
|
|
153
153
|
debounce: debounceDelay = 500,
|
|
154
154
|
onSnapStart,
|
|
155
155
|
onSnapComplete
|
|
@@ -161,15 +161,18 @@ var Snap = class {
|
|
|
161
161
|
easing,
|
|
162
162
|
duration,
|
|
163
163
|
distanceThreshold,
|
|
164
|
-
// velocityThreshold,
|
|
165
164
|
debounce: debounceDelay,
|
|
166
165
|
onSnapStart,
|
|
167
166
|
onSnapComplete
|
|
168
167
|
};
|
|
169
168
|
this.onWindowResize();
|
|
170
169
|
window.addEventListener("resize", this.onWindowResize, false);
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
if (this.options.type === "slide") {
|
|
171
|
+
this.lenis.on("scroll", this.onSlide);
|
|
172
|
+
} else {
|
|
173
|
+
this.onSnapDebounced = debounce(this.onSnap, this.options.debounce);
|
|
174
|
+
this.lenis.on("virtual-scroll", this.onSnapDebounced);
|
|
175
|
+
}
|
|
173
176
|
}
|
|
174
177
|
options;
|
|
175
178
|
elements = /* @__PURE__ */ new Map();
|
|
@@ -180,6 +183,7 @@ var Snap = class {
|
|
|
180
183
|
};
|
|
181
184
|
isStopped = false;
|
|
182
185
|
onSnapDebounced;
|
|
186
|
+
currentSnapIndex = 0;
|
|
183
187
|
/**
|
|
184
188
|
* Destroy the snap instance
|
|
185
189
|
*/
|
|
@@ -207,9 +211,9 @@ var Snap = class {
|
|
|
207
211
|
* @param userData User data that will be forwarded through the snap event
|
|
208
212
|
* @returns Unsubscribe function
|
|
209
213
|
*/
|
|
210
|
-
add(value
|
|
214
|
+
add(value) {
|
|
211
215
|
const id = uid();
|
|
212
|
-
this.snaps.set(id, { value
|
|
216
|
+
this.snaps.set(id, { value });
|
|
213
217
|
return () => this.snaps.delete(id);
|
|
214
218
|
}
|
|
215
219
|
/**
|
|
@@ -228,33 +232,8 @@ var Snap = class {
|
|
|
228
232
|
this.viewport.width = window.innerWidth;
|
|
229
233
|
this.viewport.height = window.innerHeight;
|
|
230
234
|
};
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
// // limit,
|
|
234
|
-
// lastVelocity,
|
|
235
|
-
// velocity,
|
|
236
|
-
// // isScrolling,
|
|
237
|
-
// userData,
|
|
238
|
-
// }: // isHorizontal,
|
|
239
|
-
// Lenis) => {
|
|
240
|
-
// if (this.isStopped) return
|
|
241
|
-
// // return
|
|
242
|
-
// const isDecelerating = Math.abs(lastVelocity) > Math.abs(velocity)
|
|
243
|
-
// const isTurningBack =
|
|
244
|
-
// Math.sign(lastVelocity) !== Math.sign(velocity) && velocity !== 0
|
|
245
|
-
// if (
|
|
246
|
-
// Math.abs(velocity) < this.options.velocityThreshold &&
|
|
247
|
-
// // !isTouching &&
|
|
248
|
-
// isDecelerating &&
|
|
249
|
-
// !isTurningBack &&
|
|
250
|
-
// userData?.initiator !== 'snap'
|
|
251
|
-
// ) {
|
|
252
|
-
// this.onSnapDebounced()
|
|
253
|
-
// }
|
|
254
|
-
// }
|
|
255
|
-
onSnap = () => {
|
|
256
|
-
let { scroll, isHorizontal } = this.lenis;
|
|
257
|
-
scroll = Math.ceil(this.lenis.scroll);
|
|
235
|
+
computeSnaps = () => {
|
|
236
|
+
const { isHorizontal } = this.lenis;
|
|
258
237
|
let snaps = [...this.snaps.values()];
|
|
259
238
|
this.elements.forEach(({ rect, align }) => {
|
|
260
239
|
let value;
|
|
@@ -267,11 +246,47 @@ var Snap = class {
|
|
|
267
246
|
value = isHorizontal ? rect.left + rect.width - this.viewport.width : rect.top + rect.height - this.viewport.height;
|
|
268
247
|
}
|
|
269
248
|
if (typeof value === "number") {
|
|
270
|
-
snaps.push({ value: Math.ceil(value)
|
|
249
|
+
snaps.push({ value: Math.ceil(value) });
|
|
271
250
|
}
|
|
272
251
|
});
|
|
273
252
|
});
|
|
274
253
|
snaps = snaps.sort((a, b) => Math.abs(a.value) - Math.abs(b.value));
|
|
254
|
+
return snaps;
|
|
255
|
+
};
|
|
256
|
+
onSlide = () => {
|
|
257
|
+
const { direction, userData } = this.lenis;
|
|
258
|
+
if (userData?.initiator === "snap") return;
|
|
259
|
+
const snaps = this.computeSnaps();
|
|
260
|
+
if (snaps.length === 0) return;
|
|
261
|
+
if (direction === 1) {
|
|
262
|
+
this.currentSnapIndex += 1;
|
|
263
|
+
} else if (direction === -1) {
|
|
264
|
+
this.currentSnapIndex -= 1;
|
|
265
|
+
}
|
|
266
|
+
this.currentSnapIndex = Math.max(
|
|
267
|
+
0,
|
|
268
|
+
Math.min(this.currentSnapIndex, snaps.length - 1)
|
|
269
|
+
);
|
|
270
|
+
const currentSnap = snaps[this.currentSnapIndex];
|
|
271
|
+
if (currentSnap === void 0) return;
|
|
272
|
+
this.lenis.scrollTo(currentSnap.value, {
|
|
273
|
+
duration: this.options.duration,
|
|
274
|
+
easing: this.options.easing,
|
|
275
|
+
lerp: this.options.lerp,
|
|
276
|
+
lock: true,
|
|
277
|
+
userData: { initiator: "snap" },
|
|
278
|
+
onStart: () => {
|
|
279
|
+
this.options.onSnapStart?.(currentSnap);
|
|
280
|
+
},
|
|
281
|
+
onComplete: () => {
|
|
282
|
+
this.options.onSnapComplete?.(currentSnap);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
};
|
|
286
|
+
onSnap = () => {
|
|
287
|
+
let { scroll, isHorizontal } = this.lenis;
|
|
288
|
+
scroll = Math.ceil(this.lenis.scroll);
|
|
289
|
+
const snaps = this.computeSnaps();
|
|
275
290
|
if (snaps.length === 0) return;
|
|
276
291
|
let prevSnap = snaps.findLast(({ value }) => value <= scroll);
|
|
277
292
|
if (prevSnap === void 0) prevSnap = snaps[0];
|
package/dist/lenis-snap.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../packages/snap/src/debounce.ts","../packages/snap/src/element.ts","../packages/snap/src/uid.ts","../packages/snap/src/snap.ts","../packages/snap/browser.ts"],"sourcesContent":["export function debounce<CB extends (...args: any[]) => void>(\r\n callback: CB,\r\n delay: number\r\n) {\r\n let timer: number | undefined\r\n return function <T>(this: T, ...args: Parameters<typeof callback>) {\r\n let context = this\r\n clearTimeout(timer)\r\n timer = setTimeout(() => {\r\n timer = undefined\r\n callback.apply(context, args)\r\n }, delay)\r\n }\r\n}\r\n","function removeParentSticky(element: HTMLElement) {\r\n const position = getComputedStyle(element).position\r\n\r\n const isSticky = position === 'sticky'\r\n\r\n if (isSticky) {\r\n element.style.setProperty('position', 'static')\r\n element.dataset.sticky = 'true'\r\n }\r\n\r\n if (element.offsetParent) {\r\n removeParentSticky(element.offsetParent as HTMLElement)\r\n }\r\n}\r\n\r\nfunction addParentSticky(element: HTMLElement) {\r\n if (element?.dataset?.sticky === 'true') {\r\n element.style.removeProperty('position')\r\n delete element.dataset.sticky\r\n }\r\n\r\n if (element.offsetParent) {\r\n addParentSticky(element.offsetParent as HTMLElement)\r\n }\r\n}\r\n\r\nfunction offsetTop(element: HTMLElement, accumulator = 0) {\r\n const top = accumulator + element.offsetTop\r\n if (element.offsetParent) {\r\n return offsetTop(element.offsetParent as HTMLElement, top)\r\n }\r\n return top\r\n}\r\n\r\nfunction offsetLeft(element: HTMLElement, accumulator = 0) {\r\n const left = accumulator + element.offsetLeft\r\n if (element.offsetParent) {\r\n return offsetLeft(element.offsetParent as HTMLElement, left)\r\n }\r\n return left\r\n}\r\n\r\nfunction scrollTop(element: HTMLElement, accumulator = 0) {\r\n const top = accumulator + element.scrollTop\r\n if (element.offsetParent) {\r\n return scrollTop(element.offsetParent as HTMLElement, top)\r\n }\r\n return top + window.scrollY\r\n}\r\n\r\nfunction scrollLeft(element: HTMLElement, accumulator = 0) {\r\n const left = accumulator + element.scrollLeft\r\n if (element.offsetParent) {\r\n return scrollLeft(element.offsetParent as HTMLElement, left)\r\n }\r\n return left + window.scrollX\r\n}\r\n\r\nexport type SnapElementOptions = {\r\n align?: string | string[]\r\n ignoreSticky?: boolean\r\n ignoreTransform?: boolean\r\n}\r\n\r\ntype Rect = {\r\n top: number\r\n left: number\r\n width: number\r\n height: number\r\n x: number\r\n y: number\r\n bottom: number\r\n right: number\r\n element: HTMLElement\r\n}\r\n\r\nexport class SnapElement {\r\n element: HTMLElement\r\n options: SnapElementOptions\r\n align: string[]\r\n // @ts-ignore\r\n rect: Rect = {}\r\n wrapperResizeObserver: ResizeObserver\r\n resizeObserver: ResizeObserver\r\n\r\n constructor(\r\n element: HTMLElement,\r\n {\r\n align = ['start'],\r\n ignoreSticky = true,\r\n ignoreTransform = false,\r\n }: SnapElementOptions = {}\r\n ) {\r\n this.element = element\r\n\r\n this.options = { align, ignoreSticky, ignoreTransform }\r\n\r\n // this.ignoreSticky = ignoreSticky\r\n // this.ignoreTransform = ignoreTransform\r\n\r\n this.align = [align].flat()\r\n\r\n // TODO: assing rect immediately\r\n\r\n this.wrapperResizeObserver = new ResizeObserver(this.onWrapperResize)\r\n this.wrapperResizeObserver.observe(document.body)\r\n this.onWrapperResize()\r\n\r\n this.resizeObserver = new ResizeObserver(this.onResize)\r\n this.resizeObserver.observe(this.element)\r\n this.setRect({\r\n width: this.element.offsetWidth,\r\n height: this.element.offsetHeight,\r\n })\r\n }\r\n\r\n destroy() {\r\n this.wrapperResizeObserver.disconnect()\r\n this.resizeObserver.disconnect()\r\n }\r\n\r\n setRect({\r\n top,\r\n left,\r\n width,\r\n height,\r\n element,\r\n }: {\r\n top?: number\r\n left?: number\r\n width?: number\r\n height?: number\r\n element?: HTMLElement\r\n } = {}) {\r\n top = top ?? this.rect.top\r\n left = left ?? this.rect.left\r\n width = width ?? this.rect.width\r\n height = height ?? this.rect.height\r\n element = element ?? this.rect.element\r\n\r\n if (\r\n top === this.rect.top &&\r\n left === this.rect.left &&\r\n width === this.rect.width &&\r\n height === this.rect.height &&\r\n element === this.rect.element\r\n )\r\n return\r\n\r\n this.rect.top = top\r\n this.rect.y = top\r\n this.rect.width = width\r\n this.rect.height = height\r\n this.rect.left = left\r\n this.rect.x = left\r\n this.rect.bottom = top + height\r\n this.rect.right = left + width\r\n }\r\n\r\n onWrapperResize = () => {\r\n let top, left\r\n\r\n if (this.options.ignoreSticky) removeParentSticky(this.element)\r\n if (this.options.ignoreTransform) {\r\n top = offsetTop(this.element)\r\n left = offsetLeft(this.element)\r\n } else {\r\n const rect = this.element.getBoundingClientRect()\r\n top = rect.top + scrollTop(this.element)\r\n left = rect.left + scrollLeft(this.element)\r\n }\r\n if (this.options.ignoreSticky) addParentSticky(this.element)\r\n\r\n this.setRect({ top, left })\r\n }\r\n\r\n onResize = ([entry]: ResizeObserverEntry[]) => {\r\n if (!entry?.borderBoxSize[0]) return\r\n const width = entry.borderBoxSize[0].inlineSize\r\n const height = entry.borderBoxSize[0].blockSize\r\n\r\n this.setRect({ width, height })\r\n }\r\n}\r\n","let index = 0\r\n\r\nexport type UID = number\r\n\r\nexport function uid(): UID {\r\n return index++\r\n}\r\n","import type Lenis from 'lenis'\r\nimport type { UserData } from 'lenis'\r\nimport { debounce } from './debounce'\r\nimport type { SnapElementOptions } from './element'\r\nimport { SnapElement } from './element'\r\nimport type { SnapItem, SnapOptions } from './types'\r\nimport type { UID } from './uid'\r\nimport { uid } from './uid'\r\n\r\n// TODO:\r\n// - horizontal\r\n// - fix trackpad snapping too soon due to velocity (fuck Apple)\r\n// - fix wheel scrolling after limits (see console scroll to)\r\n// - fix touch scroll, do not snap when not released\r\n// - arrow, spacebar\r\n\r\ntype RequiredPick<T, F extends keyof T> = Omit<T, F> & Required<Pick<T, F>>\r\n\r\n/**\r\n * Snap class to handle the snap functionality\r\n *\r\n * @example\r\n * const snap = new Snap(lenis, {\r\n * type: 'mandatory', // 'mandatory', 'proximity'\r\n * lerp: 0.1,\r\n * duration: 1,\r\n * easing: (t) => t,\r\n * onSnapStart: (snap) => {\r\n * console.log('onSnapStart', snap)\r\n * },\r\n * onSnapComplete: (snap) => {\r\n * console.log('onSnapComplete', snap)\r\n * },\r\n * })\r\n *\r\n * snap.add(500) // snap at 500px\r\n *\r\n * const removeSnap = snap.add(500)\r\n *\r\n * if (someCondition) {\r\n * removeSnap()\r\n * }\r\n */\r\nexport class Snap {\r\n options: RequiredPick<\r\n SnapOptions,\r\n | 'type'\r\n // | 'velocityThreshold'\r\n | 'debounce'\r\n >\r\n elements = new Map<UID, SnapElement>()\r\n snaps = new Map<UID, SnapItem>()\r\n viewport = {\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n }\r\n isStopped = false\r\n onSnapDebounced: () => void\r\n\r\n constructor(\r\n private lenis: Lenis,\r\n {\r\n type = 'proximity',\r\n lerp,\r\n easing,\r\n duration,\r\n distanceThreshold = '50%',\r\n // velocityThreshold = 1.2,\r\n debounce: debounceDelay = 500,\r\n onSnapStart,\r\n onSnapComplete,\r\n }: SnapOptions = {}\r\n ) {\r\n this.options = {\r\n type,\r\n lerp,\r\n easing,\r\n duration,\r\n distanceThreshold,\r\n // velocityThreshold,\r\n debounce: debounceDelay,\r\n onSnapStart,\r\n onSnapComplete,\r\n }\r\n\r\n this.onWindowResize()\r\n window.addEventListener('resize', this.onWindowResize, false)\r\n\r\n this.onSnapDebounced = debounce(this.onSnap, this.options.debounce)\r\n\r\n // this.lenis.on('scroll', this.onScroll)\r\n this.lenis.on('virtual-scroll', this.onSnapDebounced)\r\n }\r\n\r\n /**\r\n * Destroy the snap instance\r\n */\r\n destroy() {\r\n // this.lenis.off('scroll', this.onScroll)\r\n this.lenis.off('virtual-scroll', this.onSnapDebounced)\r\n window.removeEventListener('resize', this.onWindowResize, false)\r\n this.elements.forEach((element) => element.destroy())\r\n }\r\n\r\n /**\r\n * Start the snap after it has been stopped\r\n */\r\n start() {\r\n this.isStopped = false\r\n }\r\n\r\n /**\r\n * Stop the snap\r\n */\r\n stop() {\r\n this.isStopped = true\r\n }\r\n\r\n /**\r\n * Add a snap to the snap instance\r\n *\r\n * @param value The value to snap to\r\n * @param userData User data that will be forwarded through the snap event\r\n * @returns Unsubscribe function\r\n */\r\n add(value: number, userData: UserData = {}) {\r\n const id = uid()\r\n\r\n this.snaps.set(id, { value, userData })\r\n\r\n return () => this.snaps.delete(id)\r\n }\r\n\r\n /**\r\n * Add an element to the snap instance\r\n *\r\n * @param element The element to add\r\n * @param options The options for the element\r\n * @returns Unsubscribe function\r\n */\r\n addElement(element: HTMLElement, options: SnapElementOptions = {}) {\r\n const id = uid()\r\n\r\n this.elements.set(id, new SnapElement(element, options))\r\n\r\n return () => this.elements.delete(id)\r\n }\r\n\r\n private onWindowResize = () => {\r\n this.viewport.width = window.innerWidth\r\n this.viewport.height = window.innerHeight\r\n }\r\n\r\n // private onScroll = ({\r\n // // scroll,\r\n // // limit,\r\n // lastVelocity,\r\n // velocity,\r\n // // isScrolling,\r\n // userData,\r\n // }: // isHorizontal,\r\n // Lenis) => {\r\n // if (this.isStopped) return\r\n\r\n // // return\r\n // const isDecelerating = Math.abs(lastVelocity) > Math.abs(velocity)\r\n // const isTurningBack =\r\n // Math.sign(lastVelocity) !== Math.sign(velocity) && velocity !== 0\r\n\r\n // if (\r\n // Math.abs(velocity) < this.options.velocityThreshold &&\r\n // // !isTouching &&\r\n // isDecelerating &&\r\n // !isTurningBack &&\r\n // userData?.initiator !== 'snap'\r\n // ) {\r\n // this.onSnapDebounced()\r\n // }\r\n // }\r\n\r\n private onSnap = () => {\r\n let { scroll, isHorizontal } = this.lenis\r\n scroll = Math.ceil(this.lenis.scroll)\r\n\r\n let snaps = [...this.snaps.values()] as SnapItem[]\r\n\r\n this.elements.forEach(({ rect, align }) => {\r\n let value: number | undefined\r\n\r\n align.forEach((align) => {\r\n if (align === 'start') {\r\n value = rect.top\r\n } else if (align === 'center') {\r\n value = isHorizontal\r\n ? rect.left + rect.width / 2 - this.viewport.width / 2\r\n : rect.top + rect.height / 2 - this.viewport.height / 2\r\n } else if (align === 'end') {\r\n value = isHorizontal\r\n ? rect.left + rect.width - this.viewport.width\r\n : rect.top + rect.height - this.viewport.height\r\n }\r\n\r\n if (typeof value === 'number') {\r\n snaps.push({ value: Math.ceil(value), userData: {} })\r\n }\r\n })\r\n })\r\n\r\n snaps = snaps.sort((a, b) => Math.abs(a.value) - Math.abs(b.value))\r\n\r\n if (snaps.length === 0) return\r\n\r\n let prevSnap = snaps.findLast(({ value }) => value <= scroll)\r\n if (prevSnap === undefined) prevSnap = snaps[0]!\r\n const distanceToPrevSnap = Math.abs(scroll - prevSnap.value)\r\n\r\n let nextSnap = snaps.find(({ value }) => value >= scroll)\r\n if (nextSnap === undefined) nextSnap = snaps[snaps.length - 1]!\r\n const distanceToNextSnap = Math.abs(scroll - nextSnap.value)\r\n\r\n const snap = distanceToPrevSnap < distanceToNextSnap ? prevSnap : nextSnap\r\n\r\n const distance = Math.abs(scroll - snap.value)\r\n\r\n let distanceThreshold\r\n\r\n const axis = isHorizontal ? 'width' : 'height'\r\n\r\n if (\r\n typeof this.options.distanceThreshold === 'string' &&\r\n this.options.distanceThreshold.endsWith('%')\r\n ) {\r\n distanceThreshold =\r\n (Number(this.options.distanceThreshold.replace('%', '')) / 100) *\r\n this.viewport[axis]\r\n } else if (typeof this.options.distanceThreshold === 'number') {\r\n distanceThreshold = this.options.distanceThreshold\r\n } else {\r\n distanceThreshold = this.viewport[axis]\r\n }\r\n\r\n if (\r\n this.options.type === 'mandatory' ||\r\n (this.options.type === 'proximity' && distance <= distanceThreshold)\r\n ) {\r\n // this.__isScrolling = true\r\n // this.onSnapStart?.(snap)\r\n\r\n // console.log('scroll to')\r\n\r\n this.lenis.scrollTo(snap.value, {\r\n lerp: this.options.lerp,\r\n easing: this.options.easing,\r\n duration: this.options.duration,\r\n userData: { initiator: 'snap' },\r\n onStart: () => {\r\n this.options.onSnapStart?.(snap)\r\n },\r\n onComplete: () => {\r\n this.options.onSnapComplete?.(snap)\r\n },\r\n })\r\n }\r\n\r\n // console.timeEnd('scroll')\r\n }\r\n}\r\n","// This file serves as an entry point for the package\r\nimport { Snap } from './src/snap'\r\nglobalThis.Snap = Snap\r\n"],"mappings":"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","names":["align"]}
|
|
1
|
+
{"version":3,"sources":["../packages/snap/src/debounce.ts","../packages/snap/src/element.ts","../packages/snap/src/uid.ts","../packages/snap/src/snap.ts","../packages/snap/browser.ts"],"sourcesContent":["export function debounce<CB extends (...args: any[]) => void>(\r\n callback: CB,\r\n delay: number\r\n) {\r\n let timer: number | undefined\r\n return function <T>(this: T, ...args: Parameters<typeof callback>) {\r\n let context = this\r\n clearTimeout(timer)\r\n timer = setTimeout(() => {\r\n timer = undefined\r\n callback.apply(context, args)\r\n }, delay)\r\n }\r\n}\r\n","function removeParentSticky(element: HTMLElement) {\r\n const position = getComputedStyle(element).position\r\n\r\n const isSticky = position === 'sticky'\r\n\r\n if (isSticky) {\r\n element.style.setProperty('position', 'static')\r\n element.dataset.sticky = 'true'\r\n }\r\n\r\n if (element.offsetParent) {\r\n removeParentSticky(element.offsetParent as HTMLElement)\r\n }\r\n}\r\n\r\nfunction addParentSticky(element: HTMLElement) {\r\n if (element?.dataset?.sticky === 'true') {\r\n element.style.removeProperty('position')\r\n delete element.dataset.sticky\r\n }\r\n\r\n if (element.offsetParent) {\r\n addParentSticky(element.offsetParent as HTMLElement)\r\n }\r\n}\r\n\r\nfunction offsetTop(element: HTMLElement, accumulator = 0) {\r\n const top = accumulator + element.offsetTop\r\n if (element.offsetParent) {\r\n return offsetTop(element.offsetParent as HTMLElement, top)\r\n }\r\n return top\r\n}\r\n\r\nfunction offsetLeft(element: HTMLElement, accumulator = 0) {\r\n const left = accumulator + element.offsetLeft\r\n if (element.offsetParent) {\r\n return offsetLeft(element.offsetParent as HTMLElement, left)\r\n }\r\n return left\r\n}\r\n\r\nfunction scrollTop(element: HTMLElement, accumulator = 0) {\r\n const top = accumulator + element.scrollTop\r\n if (element.offsetParent) {\r\n return scrollTop(element.offsetParent as HTMLElement, top)\r\n }\r\n return top + window.scrollY\r\n}\r\n\r\nfunction scrollLeft(element: HTMLElement, accumulator = 0) {\r\n const left = accumulator + element.scrollLeft\r\n if (element.offsetParent) {\r\n return scrollLeft(element.offsetParent as HTMLElement, left)\r\n }\r\n return left + window.scrollX\r\n}\r\n\r\nexport type SnapElementOptions = {\r\n align?: string | string[]\r\n ignoreSticky?: boolean\r\n ignoreTransform?: boolean\r\n}\r\n\r\ntype Rect = {\r\n top: number\r\n left: number\r\n width: number\r\n height: number\r\n x: number\r\n y: number\r\n bottom: number\r\n right: number\r\n element: HTMLElement\r\n}\r\n\r\nexport class SnapElement {\r\n element: HTMLElement\r\n options: SnapElementOptions\r\n align: string[]\r\n // @ts-ignore\r\n rect: Rect = {}\r\n wrapperResizeObserver: ResizeObserver\r\n resizeObserver: ResizeObserver\r\n\r\n constructor(\r\n element: HTMLElement,\r\n {\r\n align = ['start'],\r\n ignoreSticky = true,\r\n ignoreTransform = false,\r\n }: SnapElementOptions = {}\r\n ) {\r\n this.element = element\r\n\r\n this.options = { align, ignoreSticky, ignoreTransform }\r\n\r\n // this.ignoreSticky = ignoreSticky\r\n // this.ignoreTransform = ignoreTransform\r\n\r\n this.align = [align].flat()\r\n\r\n // TODO: assing rect immediately\r\n\r\n this.wrapperResizeObserver = new ResizeObserver(this.onWrapperResize)\r\n this.wrapperResizeObserver.observe(document.body)\r\n this.onWrapperResize()\r\n\r\n this.resizeObserver = new ResizeObserver(this.onResize)\r\n this.resizeObserver.observe(this.element)\r\n this.setRect({\r\n width: this.element.offsetWidth,\r\n height: this.element.offsetHeight,\r\n })\r\n }\r\n\r\n destroy() {\r\n this.wrapperResizeObserver.disconnect()\r\n this.resizeObserver.disconnect()\r\n }\r\n\r\n setRect({\r\n top,\r\n left,\r\n width,\r\n height,\r\n element,\r\n }: {\r\n top?: number\r\n left?: number\r\n width?: number\r\n height?: number\r\n element?: HTMLElement\r\n } = {}) {\r\n top = top ?? this.rect.top\r\n left = left ?? this.rect.left\r\n width = width ?? this.rect.width\r\n height = height ?? this.rect.height\r\n element = element ?? this.rect.element\r\n\r\n if (\r\n top === this.rect.top &&\r\n left === this.rect.left &&\r\n width === this.rect.width &&\r\n height === this.rect.height &&\r\n element === this.rect.element\r\n )\r\n return\r\n\r\n this.rect.top = top\r\n this.rect.y = top\r\n this.rect.width = width\r\n this.rect.height = height\r\n this.rect.left = left\r\n this.rect.x = left\r\n this.rect.bottom = top + height\r\n this.rect.right = left + width\r\n }\r\n\r\n onWrapperResize = () => {\r\n let top, left\r\n\r\n if (this.options.ignoreSticky) removeParentSticky(this.element)\r\n if (this.options.ignoreTransform) {\r\n top = offsetTop(this.element)\r\n left = offsetLeft(this.element)\r\n } else {\r\n const rect = this.element.getBoundingClientRect()\r\n top = rect.top + scrollTop(this.element)\r\n left = rect.left + scrollLeft(this.element)\r\n }\r\n if (this.options.ignoreSticky) addParentSticky(this.element)\r\n\r\n this.setRect({ top, left })\r\n }\r\n\r\n onResize = ([entry]: ResizeObserverEntry[]) => {\r\n if (!entry?.borderBoxSize[0]) return\r\n const width = entry.borderBoxSize[0].inlineSize\r\n const height = entry.borderBoxSize[0].blockSize\r\n\r\n this.setRect({ width, height })\r\n }\r\n}\r\n","let index = 0\r\n\r\nexport type UID = number\r\n\r\nexport function uid(): UID {\r\n return index++\r\n}\r\n","import type Lenis from 'lenis'\r\nimport { debounce } from './debounce'\r\nimport type { SnapElementOptions } from './element'\r\nimport { SnapElement } from './element'\r\nimport type { SnapItem, SnapOptions } from './types'\r\nimport type { UID } from './uid'\r\nimport { uid } from './uid'\r\n\r\n// TODO:\r\n// - horizontal\r\n// - fix trackpad snapping too soon due to velocity (fuck Apple)\r\n// - fix wheel scrolling after limits (see console scroll to)\r\n// - fix touch scroll, do not snap when not released\r\n// - arrow, spacebar\r\n\r\ntype RequiredPick<T, F extends keyof T> = Omit<T, F> & Required<Pick<T, F>>\r\n\r\n/**\r\n * Snap class to handle the snap functionality\r\n *\r\n * @example\r\n * const snap = new Snap(lenis, {\r\n * type: 'mandatory', // 'mandatory', 'proximity'\r\n * lerp: 0.1,\r\n * duration: 1,\r\n * easing: (t) => t,\r\n * onSnapStart: (snap) => {\r\n * console.log('onSnapStart', snap)\r\n * },\r\n * onSnapComplete: (snap) => {\r\n * console.log('onSnapComplete', snap)\r\n * },\r\n * })\r\n *\r\n * snap.add(500) // snap at 500px\r\n *\r\n * const removeSnap = snap.add(500)\r\n *\r\n * if (someCondition) {\r\n * removeSnap()\r\n * }\r\n */\r\nexport class Snap {\r\n options: RequiredPick<SnapOptions, 'type' | 'debounce'>\r\n elements = new Map<UID, SnapElement>()\r\n snaps = new Map<UID, SnapItem>()\r\n viewport: { width: number; height: number } = {\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n }\r\n isStopped = false\r\n onSnapDebounced?: () => void\r\n currentSnapIndex = 0 // used for slide type\r\n\r\n constructor(\r\n private lenis: Lenis,\r\n {\r\n type = 'proximity',\r\n lerp,\r\n easing,\r\n duration,\r\n distanceThreshold = '50%',\r\n debounce: debounceDelay = 500,\r\n onSnapStart,\r\n onSnapComplete,\r\n }: SnapOptions = {}\r\n ) {\r\n this.options = {\r\n type,\r\n lerp,\r\n easing,\r\n duration,\r\n distanceThreshold,\r\n debounce: debounceDelay,\r\n onSnapStart,\r\n onSnapComplete,\r\n }\r\n\r\n this.onWindowResize()\r\n window.addEventListener('resize', this.onWindowResize, false)\r\n\r\n if (this.options.type === 'slide') {\r\n this.lenis.on('scroll', this.onSlide)\r\n } else {\r\n this.onSnapDebounced = debounce(this.onSnap, this.options.debounce)\r\n\r\n // this.lenis.on('scroll', this.onScroll)\r\n this.lenis.on('virtual-scroll', this.onSnapDebounced)\r\n }\r\n }\r\n\r\n /**\r\n * Destroy the snap instance\r\n */\r\n destroy() {\r\n // this.lenis.off('scroll', this.onScroll)\r\n this.lenis.off('virtual-scroll', this.onSnapDebounced!)\r\n window.removeEventListener('resize', this.onWindowResize, false)\r\n this.elements.forEach((element) => element.destroy())\r\n }\r\n\r\n /**\r\n * Start the snap after it has been stopped\r\n */\r\n start() {\r\n this.isStopped = false\r\n }\r\n\r\n /**\r\n * Stop the snap\r\n */\r\n stop() {\r\n this.isStopped = true\r\n }\r\n\r\n /**\r\n * Add a snap to the snap instance\r\n *\r\n * @param value The value to snap to\r\n * @param userData User data that will be forwarded through the snap event\r\n * @returns Unsubscribe function\r\n */\r\n add(value: number) {\r\n const id = uid()\r\n\r\n this.snaps.set(id, { value })\r\n\r\n return () => this.snaps.delete(id)\r\n }\r\n\r\n /**\r\n * Add an element to the snap instance\r\n *\r\n * @param element The element to add\r\n * @param options The options for the element\r\n * @returns Unsubscribe function\r\n */\r\n addElement(element: HTMLElement, options: SnapElementOptions = {}) {\r\n const id = uid()\r\n\r\n this.elements.set(id, new SnapElement(element, options))\r\n\r\n return () => this.elements.delete(id)\r\n }\r\n\r\n private onWindowResize = () => {\r\n this.viewport.width = window.innerWidth\r\n this.viewport.height = window.innerHeight\r\n }\r\n\r\n private computeSnaps = () => {\r\n const { isHorizontal } = this.lenis\r\n\r\n let snaps = [...this.snaps.values()] as SnapItem[]\r\n\r\n this.elements.forEach(({ rect, align }) => {\r\n let value: number | undefined\r\n\r\n align.forEach((align) => {\r\n if (align === 'start') {\r\n value = rect.top\r\n } else if (align === 'center') {\r\n value = isHorizontal\r\n ? rect.left + rect.width / 2 - this.viewport.width / 2\r\n : rect.top + rect.height / 2 - this.viewport.height / 2\r\n } else if (align === 'end') {\r\n value = isHorizontal\r\n ? rect.left + rect.width - this.viewport.width\r\n : rect.top + rect.height - this.viewport.height\r\n }\r\n\r\n if (typeof value === 'number') {\r\n snaps.push({ value: Math.ceil(value) })\r\n }\r\n })\r\n })\r\n\r\n snaps = snaps.sort((a, b) => Math.abs(a.value) - Math.abs(b.value))\r\n\r\n return snaps\r\n }\r\n\r\n private onSlide = () => {\r\n const { direction, userData } = this.lenis\r\n\r\n if (userData?.initiator === 'snap') return\r\n\r\n const snaps = this.computeSnaps()\r\n\r\n if (snaps.length === 0) return\r\n\r\n if (direction === 1) {\r\n this.currentSnapIndex += 1\r\n } else if (direction === -1) {\r\n this.currentSnapIndex -= 1\r\n }\r\n\r\n this.currentSnapIndex = Math.max(\r\n 0,\r\n Math.min(this.currentSnapIndex, snaps.length - 1)\r\n )\r\n\r\n const currentSnap = snaps[this.currentSnapIndex]\r\n\r\n if (currentSnap === undefined) return\r\n\r\n this.lenis.scrollTo(currentSnap.value, {\r\n duration: this.options.duration,\r\n easing: this.options.easing,\r\n lerp: this.options.lerp,\r\n lock: true,\r\n userData: { initiator: 'snap' },\r\n onStart: () => {\r\n this.options.onSnapStart?.(currentSnap)\r\n },\r\n onComplete: () => {\r\n this.options.onSnapComplete?.(currentSnap)\r\n },\r\n })\r\n }\r\n\r\n private onSnap = () => {\r\n let { scroll, isHorizontal } = this.lenis\r\n scroll = Math.ceil(this.lenis.scroll)\r\n\r\n const snaps = this.computeSnaps()\r\n\r\n if (snaps.length === 0) return\r\n\r\n let prevSnap = snaps.findLast(({ value }) => value <= scroll)\r\n if (prevSnap === undefined) prevSnap = snaps[0]!\r\n const distanceToPrevSnap = Math.abs(scroll - prevSnap.value)\r\n\r\n let nextSnap = snaps.find(({ value }) => value >= scroll)\r\n if (nextSnap === undefined) nextSnap = snaps[snaps.length - 1]!\r\n const distanceToNextSnap = Math.abs(scroll - nextSnap.value)\r\n\r\n const snap = distanceToPrevSnap < distanceToNextSnap ? prevSnap : nextSnap\r\n\r\n const distance = Math.abs(scroll - snap.value)\r\n\r\n let distanceThreshold\r\n\r\n const axis = isHorizontal ? 'width' : 'height'\r\n\r\n if (\r\n typeof this.options.distanceThreshold === 'string' &&\r\n this.options.distanceThreshold.endsWith('%')\r\n ) {\r\n distanceThreshold =\r\n (Number(this.options.distanceThreshold.replace('%', '')) / 100) *\r\n this.viewport[axis]\r\n } else if (typeof this.options.distanceThreshold === 'number') {\r\n distanceThreshold = this.options.distanceThreshold\r\n } else {\r\n distanceThreshold = this.viewport[axis]\r\n }\r\n\r\n if (\r\n this.options.type === 'mandatory' ||\r\n (this.options.type === 'proximity' && distance <= distanceThreshold)\r\n ) {\r\n // this.__isScrolling = true\r\n // this.onSnapStart?.(snap)\r\n\r\n // console.log('scroll to')\r\n\r\n this.lenis.scrollTo(snap.value, {\r\n lerp: this.options.lerp,\r\n easing: this.options.easing,\r\n duration: this.options.duration,\r\n userData: { initiator: 'snap' },\r\n onStart: () => {\r\n this.options.onSnapStart?.(snap)\r\n },\r\n onComplete: () => {\r\n this.options.onSnapComplete?.(snap)\r\n },\r\n })\r\n }\r\n\r\n // console.timeEnd('scroll')\r\n }\r\n}\r\n","// This file serves as an entry point for the package\r\nimport { Snap } from './src/snap'\r\nglobalThis.Snap = Snap\r\n"],"mappings":"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","names":["align"]}
|
package/dist/lenis-snap.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function t(e){"sticky"===getComputedStyle(e).position&&(e.style.setProperty("position","static"),e.dataset.sticky="true"),e.offsetParent&&t(e.offsetParent)}function e(t){"true"===t?.dataset?.sticky&&(t.style.removeProperty("position"),delete t.dataset.sticky),t.offsetParent&&e(t.offsetParent)}function i(t,e=0){const s=e+t.offsetTop;return t.offsetParent?i(t.offsetParent,s):s}function s(t,e=0){const i=e+t.offsetLeft;return t.offsetParent?s(t.offsetParent,i):i}function n(t,e=0){const i=e+t.scrollTop;return t.offsetParent?n(t.offsetParent,i):i+window.scrollY}function o(t,e=0){const i=e+t.scrollLeft;return t.offsetParent?o(t.offsetParent,i):i+window.scrollX}var r=class{element;options;align;rect={};wrapperResizeObserver;resizeObserver;constructor(t,{align:e=["start"],ignoreSticky:i=!0,ignoreTransform:s=!1}={}){this.element=t,this.options={align:e,ignoreSticky:i,ignoreTransform:s},this.align=[e].flat(),this.wrapperResizeObserver=new ResizeObserver(this.onWrapperResize),this.wrapperResizeObserver.observe(document.body),this.onWrapperResize(),this.resizeObserver=new ResizeObserver(this.onResize),this.resizeObserver.observe(this.element),this.setRect({width:this.element.offsetWidth,height:this.element.offsetHeight})}destroy(){this.wrapperResizeObserver.disconnect(),this.resizeObserver.disconnect()}setRect({top:t,left:e,width:i,height:s,element:n}={}){t=t??this.rect.top,e=e??this.rect.left,i=i??this.rect.width,s=s??this.rect.height,n=n??this.rect.element,t===this.rect.top&&e===this.rect.left&&i===this.rect.width&&s===this.rect.height&&n===this.rect.element||(this.rect.top=t,this.rect.y=t,this.rect.width=i,this.rect.height=s,this.rect.left=e,this.rect.x=e,this.rect.bottom=t+s,this.rect.right=e+i)}onWrapperResize=()=>{let r,h;if(this.options.ignoreSticky&&t(this.element),this.options.ignoreTransform)r=i(this.element),h=s(this.element);else{const t=this.element.getBoundingClientRect();r=t.top+n(this.element),h=t.left+o(this.element)}this.options.ignoreSticky&&e(this.element),this.setRect({top:r,left:h})};onResize=([t])=>{if(!t?.borderBoxSize[0])return;const e=t.borderBoxSize[0].inlineSize,i=t.borderBoxSize[0].blockSize;this.setRect({width:e,height:i})}},h=0;function a(){return h++}globalThis.Snap=class{constructor(t,{type:e="proximity",lerp:i,easing:s,duration:n,distanceThreshold:o="50%",debounce:r=500,onSnapStart:h,onSnapComplete:a}={}){this.lenis=t,this.options={type:e,lerp:i,easing:s,duration:n,distanceThreshold:o,debounce:r,onSnapStart:h,onSnapComplete:a},this.onWindowResize(),window.addEventListener("resize",this.onWindowResize,!1),this.onSnapDebounced=function(t,e){let i;return function(...s){let n=this;clearTimeout(i),i=setTimeout((()=>{i=void 0,t.apply(n,s)}),e)}}(this.onSnap,this.options.debounce),this.lenis.on("virtual-scroll",this.onSnapDebounced)}options;elements=new Map;snaps=new Map;viewport={width:window.innerWidth,height:window.innerHeight};isStopped=!1;onSnapDebounced;destroy(){this.lenis.off("virtual-scroll",this.onSnapDebounced),window.removeEventListener("resize",this.onWindowResize,!1),this.elements.forEach((t=>t.destroy()))}start(){this.isStopped=!1}stop(){this.isStopped=!0}add(t
|
|
1
|
+
function t(e){"sticky"===getComputedStyle(e).position&&(e.style.setProperty("position","static"),e.dataset.sticky="true"),e.offsetParent&&t(e.offsetParent)}function e(t){"true"===t?.dataset?.sticky&&(t.style.removeProperty("position"),delete t.dataset.sticky),t.offsetParent&&e(t.offsetParent)}function i(t,e=0){const s=e+t.offsetTop;return t.offsetParent?i(t.offsetParent,s):s}function s(t,e=0){const i=e+t.offsetLeft;return t.offsetParent?s(t.offsetParent,i):i}function n(t,e=0){const i=e+t.scrollTop;return t.offsetParent?n(t.offsetParent,i):i+window.scrollY}function o(t,e=0){const i=e+t.scrollLeft;return t.offsetParent?o(t.offsetParent,i):i+window.scrollX}var r=class{element;options;align;rect={};wrapperResizeObserver;resizeObserver;constructor(t,{align:e=["start"],ignoreSticky:i=!0,ignoreTransform:s=!1}={}){this.element=t,this.options={align:e,ignoreSticky:i,ignoreTransform:s},this.align=[e].flat(),this.wrapperResizeObserver=new ResizeObserver(this.onWrapperResize),this.wrapperResizeObserver.observe(document.body),this.onWrapperResize(),this.resizeObserver=new ResizeObserver(this.onResize),this.resizeObserver.observe(this.element),this.setRect({width:this.element.offsetWidth,height:this.element.offsetHeight})}destroy(){this.wrapperResizeObserver.disconnect(),this.resizeObserver.disconnect()}setRect({top:t,left:e,width:i,height:s,element:n}={}){t=t??this.rect.top,e=e??this.rect.left,i=i??this.rect.width,s=s??this.rect.height,n=n??this.rect.element,t===this.rect.top&&e===this.rect.left&&i===this.rect.width&&s===this.rect.height&&n===this.rect.element||(this.rect.top=t,this.rect.y=t,this.rect.width=i,this.rect.height=s,this.rect.left=e,this.rect.x=e,this.rect.bottom=t+s,this.rect.right=e+i)}onWrapperResize=()=>{let r,h;if(this.options.ignoreSticky&&t(this.element),this.options.ignoreTransform)r=i(this.element),h=s(this.element);else{const t=this.element.getBoundingClientRect();r=t.top+n(this.element),h=t.left+o(this.element)}this.options.ignoreSticky&&e(this.element),this.setRect({top:r,left:h})};onResize=([t])=>{if(!t?.borderBoxSize[0])return;const e=t.borderBoxSize[0].inlineSize,i=t.borderBoxSize[0].blockSize;this.setRect({width:e,height:i})}},h=0;function a(){return h++}globalThis.Snap=class{constructor(t,{type:e="proximity",lerp:i,easing:s,duration:n,distanceThreshold:o="50%",debounce:r=500,onSnapStart:h,onSnapComplete:a}={}){this.lenis=t,this.options={type:e,lerp:i,easing:s,duration:n,distanceThreshold:o,debounce:r,onSnapStart:h,onSnapComplete:a},this.onWindowResize(),window.addEventListener("resize",this.onWindowResize,!1),"slide"===this.options.type?this.lenis.on("scroll",this.onSlide):(this.onSnapDebounced=function(t,e){let i;return function(...s){let n=this;clearTimeout(i),i=setTimeout((()=>{i=void 0,t.apply(n,s)}),e)}}(this.onSnap,this.options.debounce),this.lenis.on("virtual-scroll",this.onSnapDebounced))}options;elements=new Map;snaps=new Map;viewport={width:window.innerWidth,height:window.innerHeight};isStopped=!1;onSnapDebounced;currentSnapIndex=0;destroy(){this.lenis.off("virtual-scroll",this.onSnapDebounced),window.removeEventListener("resize",this.onWindowResize,!1),this.elements.forEach((t=>t.destroy()))}start(){this.isStopped=!1}stop(){this.isStopped=!0}add(t){const e=a();return this.snaps.set(e,{value:t}),()=>this.snaps.delete(e)}addElement(t,e={}){const i=a();return this.elements.set(i,new r(t,e)),()=>this.elements.delete(i)}onWindowResize=()=>{this.viewport.width=window.innerWidth,this.viewport.height=window.innerHeight};computeSnaps=()=>{const{isHorizontal:t}=this.lenis;let e=[...this.snaps.values()];return this.elements.forEach((({rect:i,align:s})=>{let n;s.forEach((s=>{"start"===s?n=i.top:"center"===s?n=t?i.left+i.width/2-this.viewport.width/2:i.top+i.height/2-this.viewport.height/2:"end"===s&&(n=t?i.left+i.width-this.viewport.width:i.top+i.height-this.viewport.height),"number"==typeof n&&e.push({value:Math.ceil(n)})}))})),e=e.sort(((t,e)=>Math.abs(t.value)-Math.abs(e.value))),e};onSlide=()=>{const{direction:t,userData:e}=this.lenis;if("snap"===e?.initiator)return;const i=this.computeSnaps();if(0===i.length)return;1===t?this.currentSnapIndex+=1:-1===t&&(this.currentSnapIndex-=1),this.currentSnapIndex=Math.max(0,Math.min(this.currentSnapIndex,i.length-1));const s=i[this.currentSnapIndex];void 0!==s&&this.lenis.scrollTo(s.value,{duration:this.options.duration,easing:this.options.easing,lerp:this.options.lerp,lock:!0,userData:{initiator:"snap"},onStart:()=>{this.options.onSnapStart?.(s)},onComplete:()=>{this.options.onSnapComplete?.(s)}})};onSnap=()=>{let{scroll:t,isHorizontal:e}=this.lenis;t=Math.ceil(this.lenis.scroll);const i=this.computeSnaps();if(0===i.length)return;let s=i.findLast((({value:e})=>e<=t));void 0===s&&(s=i[0]);const n=Math.abs(t-s.value);let o=i.find((({value:e})=>e>=t));void 0===o&&(o=i[i.length-1]);const r=n<Math.abs(t-o.value)?s:o,h=Math.abs(t-r.value);let a;const l=e?"width":"height";a="string"==typeof this.options.distanceThreshold&&this.options.distanceThreshold.endsWith("%")?Number(this.options.distanceThreshold.replace("%",""))/100*this.viewport[l]:"number"==typeof this.options.distanceThreshold?this.options.distanceThreshold:this.viewport[l],("mandatory"===this.options.type||"proximity"===this.options.type&&h<=a)&&this.lenis.scrollTo(r.value,{lerp:this.options.lerp,easing:this.options.easing,duration:this.options.duration,userData:{initiator:"snap"},onStart:()=>{this.options.onSnapStart?.(r)},onComplete:()=>{this.options.onSnapComplete?.(r)}})}};//# sourceMappingURL=lenis-snap.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../packages/snap/src/debounce.ts","../packages/snap/src/element.ts","../packages/snap/src/uid.ts","../packages/snap/src/snap.ts","../packages/snap/browser.ts"],"sourcesContent":["export function debounce<CB extends (...args: any[]) => void>(\r\n callback: CB,\r\n delay: number\r\n) {\r\n let timer: number | undefined\r\n return function <T>(this: T, ...args: Parameters<typeof callback>) {\r\n let context = this\r\n clearTimeout(timer)\r\n timer = setTimeout(() => {\r\n timer = undefined\r\n callback.apply(context, args)\r\n }, delay)\r\n }\r\n}\r\n","function removeParentSticky(element: HTMLElement) {\r\n const position = getComputedStyle(element).position\r\n\r\n const isSticky = position === 'sticky'\r\n\r\n if (isSticky) {\r\n element.style.setProperty('position', 'static')\r\n element.dataset.sticky = 'true'\r\n }\r\n\r\n if (element.offsetParent) {\r\n removeParentSticky(element.offsetParent as HTMLElement)\r\n }\r\n}\r\n\r\nfunction addParentSticky(element: HTMLElement) {\r\n if (element?.dataset?.sticky === 'true') {\r\n element.style.removeProperty('position')\r\n delete element.dataset.sticky\r\n }\r\n\r\n if (element.offsetParent) {\r\n addParentSticky(element.offsetParent as HTMLElement)\r\n }\r\n}\r\n\r\nfunction offsetTop(element: HTMLElement, accumulator = 0) {\r\n const top = accumulator + element.offsetTop\r\n if (element.offsetParent) {\r\n return offsetTop(element.offsetParent as HTMLElement, top)\r\n }\r\n return top\r\n}\r\n\r\nfunction offsetLeft(element: HTMLElement, accumulator = 0) {\r\n const left = accumulator + element.offsetLeft\r\n if (element.offsetParent) {\r\n return offsetLeft(element.offsetParent as HTMLElement, left)\r\n }\r\n return left\r\n}\r\n\r\nfunction scrollTop(element: HTMLElement, accumulator = 0) {\r\n const top = accumulator + element.scrollTop\r\n if (element.offsetParent) {\r\n return scrollTop(element.offsetParent as HTMLElement, top)\r\n }\r\n return top + window.scrollY\r\n}\r\n\r\nfunction scrollLeft(element: HTMLElement, accumulator = 0) {\r\n const left = accumulator + element.scrollLeft\r\n if (element.offsetParent) {\r\n return scrollLeft(element.offsetParent as HTMLElement, left)\r\n }\r\n return left + window.scrollX\r\n}\r\n\r\nexport type SnapElementOptions = {\r\n align?: string | string[]\r\n ignoreSticky?: boolean\r\n ignoreTransform?: boolean\r\n}\r\n\r\ntype Rect = {\r\n top: number\r\n left: number\r\n width: number\r\n height: number\r\n x: number\r\n y: number\r\n bottom: number\r\n right: number\r\n element: HTMLElement\r\n}\r\n\r\nexport class SnapElement {\r\n element: HTMLElement\r\n options: SnapElementOptions\r\n align: string[]\r\n // @ts-ignore\r\n rect: Rect = {}\r\n wrapperResizeObserver: ResizeObserver\r\n resizeObserver: ResizeObserver\r\n\r\n constructor(\r\n element: HTMLElement,\r\n {\r\n align = ['start'],\r\n ignoreSticky = true,\r\n ignoreTransform = false,\r\n }: SnapElementOptions = {}\r\n ) {\r\n this.element = element\r\n\r\n this.options = { align, ignoreSticky, ignoreTransform }\r\n\r\n // this.ignoreSticky = ignoreSticky\r\n // this.ignoreTransform = ignoreTransform\r\n\r\n this.align = [align].flat()\r\n\r\n // TODO: assing rect immediately\r\n\r\n this.wrapperResizeObserver = new ResizeObserver(this.onWrapperResize)\r\n this.wrapperResizeObserver.observe(document.body)\r\n this.onWrapperResize()\r\n\r\n this.resizeObserver = new ResizeObserver(this.onResize)\r\n this.resizeObserver.observe(this.element)\r\n this.setRect({\r\n width: this.element.offsetWidth,\r\n height: this.element.offsetHeight,\r\n })\r\n }\r\n\r\n destroy() {\r\n this.wrapperResizeObserver.disconnect()\r\n this.resizeObserver.disconnect()\r\n }\r\n\r\n setRect({\r\n top,\r\n left,\r\n width,\r\n height,\r\n element,\r\n }: {\r\n top?: number\r\n left?: number\r\n width?: number\r\n height?: number\r\n element?: HTMLElement\r\n } = {}) {\r\n top = top ?? this.rect.top\r\n left = left ?? this.rect.left\r\n width = width ?? this.rect.width\r\n height = height ?? this.rect.height\r\n element = element ?? this.rect.element\r\n\r\n if (\r\n top === this.rect.top &&\r\n left === this.rect.left &&\r\n width === this.rect.width &&\r\n height === this.rect.height &&\r\n element === this.rect.element\r\n )\r\n return\r\n\r\n this.rect.top = top\r\n this.rect.y = top\r\n this.rect.width = width\r\n this.rect.height = height\r\n this.rect.left = left\r\n this.rect.x = left\r\n this.rect.bottom = top + height\r\n this.rect.right = left + width\r\n }\r\n\r\n onWrapperResize = () => {\r\n let top, left\r\n\r\n if (this.options.ignoreSticky) removeParentSticky(this.element)\r\n if (this.options.ignoreTransform) {\r\n top = offsetTop(this.element)\r\n left = offsetLeft(this.element)\r\n } else {\r\n const rect = this.element.getBoundingClientRect()\r\n top = rect.top + scrollTop(this.element)\r\n left = rect.left + scrollLeft(this.element)\r\n }\r\n if (this.options.ignoreSticky) addParentSticky(this.element)\r\n\r\n this.setRect({ top, left })\r\n }\r\n\r\n onResize = ([entry]: ResizeObserverEntry[]) => {\r\n if (!entry?.borderBoxSize[0]) return\r\n const width = entry.borderBoxSize[0].inlineSize\r\n const height = entry.borderBoxSize[0].blockSize\r\n\r\n this.setRect({ width, height })\r\n }\r\n}\r\n","let index = 0\r\n\r\nexport type UID = number\r\n\r\nexport function uid(): UID {\r\n return index++\r\n}\r\n","import type Lenis from 'lenis'\r\nimport type { UserData } from 'lenis'\r\nimport { debounce } from './debounce'\r\nimport type { SnapElementOptions } from './element'\r\nimport { SnapElement } from './element'\r\nimport type { SnapItem, SnapOptions } from './types'\r\nimport type { UID } from './uid'\r\nimport { uid } from './uid'\r\n\r\n// TODO:\r\n// - horizontal\r\n// - fix trackpad snapping too soon due to velocity (fuck Apple)\r\n// - fix wheel scrolling after limits (see console scroll to)\r\n// - fix touch scroll, do not snap when not released\r\n// - arrow, spacebar\r\n\r\ntype RequiredPick<T, F extends keyof T> = Omit<T, F> & Required<Pick<T, F>>\r\n\r\n/**\r\n * Snap class to handle the snap functionality\r\n *\r\n * @example\r\n * const snap = new Snap(lenis, {\r\n * type: 'mandatory', // 'mandatory', 'proximity'\r\n * lerp: 0.1,\r\n * duration: 1,\r\n * easing: (t) => t,\r\n * onSnapStart: (snap) => {\r\n * console.log('onSnapStart', snap)\r\n * },\r\n * onSnapComplete: (snap) => {\r\n * console.log('onSnapComplete', snap)\r\n * },\r\n * })\r\n *\r\n * snap.add(500) // snap at 500px\r\n *\r\n * const removeSnap = snap.add(500)\r\n *\r\n * if (someCondition) {\r\n * removeSnap()\r\n * }\r\n */\r\nexport class Snap {\r\n options: RequiredPick<\r\n SnapOptions,\r\n | 'type'\r\n // | 'velocityThreshold'\r\n | 'debounce'\r\n >\r\n elements = new Map<UID, SnapElement>()\r\n snaps = new Map<UID, SnapItem>()\r\n viewport = {\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n }\r\n isStopped = false\r\n onSnapDebounced: () => void\r\n\r\n constructor(\r\n private lenis: Lenis,\r\n {\r\n type = 'proximity',\r\n lerp,\r\n easing,\r\n duration,\r\n distanceThreshold = '50%',\r\n // velocityThreshold = 1.2,\r\n debounce: debounceDelay = 500,\r\n onSnapStart,\r\n onSnapComplete,\r\n }: SnapOptions = {}\r\n ) {\r\n this.options = {\r\n type,\r\n lerp,\r\n easing,\r\n duration,\r\n distanceThreshold,\r\n // velocityThreshold,\r\n debounce: debounceDelay,\r\n onSnapStart,\r\n onSnapComplete,\r\n }\r\n\r\n this.onWindowResize()\r\n window.addEventListener('resize', this.onWindowResize, false)\r\n\r\n this.onSnapDebounced = debounce(this.onSnap, this.options.debounce)\r\n\r\n // this.lenis.on('scroll', this.onScroll)\r\n this.lenis.on('virtual-scroll', this.onSnapDebounced)\r\n }\r\n\r\n /**\r\n * Destroy the snap instance\r\n */\r\n destroy() {\r\n // this.lenis.off('scroll', this.onScroll)\r\n this.lenis.off('virtual-scroll', this.onSnapDebounced)\r\n window.removeEventListener('resize', this.onWindowResize, false)\r\n this.elements.forEach((element) => element.destroy())\r\n }\r\n\r\n /**\r\n * Start the snap after it has been stopped\r\n */\r\n start() {\r\n this.isStopped = false\r\n }\r\n\r\n /**\r\n * Stop the snap\r\n */\r\n stop() {\r\n this.isStopped = true\r\n }\r\n\r\n /**\r\n * Add a snap to the snap instance\r\n *\r\n * @param value The value to snap to\r\n * @param userData User data that will be forwarded through the snap event\r\n * @returns Unsubscribe function\r\n */\r\n add(value: number, userData: UserData = {}) {\r\n const id = uid()\r\n\r\n this.snaps.set(id, { value, userData })\r\n\r\n return () => this.snaps.delete(id)\r\n }\r\n\r\n /**\r\n * Add an element to the snap instance\r\n *\r\n * @param element The element to add\r\n * @param options The options for the element\r\n * @returns Unsubscribe function\r\n */\r\n addElement(element: HTMLElement, options: SnapElementOptions = {}) {\r\n const id = uid()\r\n\r\n this.elements.set(id, new SnapElement(element, options))\r\n\r\n return () => this.elements.delete(id)\r\n }\r\n\r\n private onWindowResize = () => {\r\n this.viewport.width = window.innerWidth\r\n this.viewport.height = window.innerHeight\r\n }\r\n\r\n // private onScroll = ({\r\n // // scroll,\r\n // // limit,\r\n // lastVelocity,\r\n // velocity,\r\n // // isScrolling,\r\n // userData,\r\n // }: // isHorizontal,\r\n // Lenis) => {\r\n // if (this.isStopped) return\r\n\r\n // // return\r\n // const isDecelerating = Math.abs(lastVelocity) > Math.abs(velocity)\r\n // const isTurningBack =\r\n // Math.sign(lastVelocity) !== Math.sign(velocity) && velocity !== 0\r\n\r\n // if (\r\n // Math.abs(velocity) < this.options.velocityThreshold &&\r\n // // !isTouching &&\r\n // isDecelerating &&\r\n // !isTurningBack &&\r\n // userData?.initiator !== 'snap'\r\n // ) {\r\n // this.onSnapDebounced()\r\n // }\r\n // }\r\n\r\n private onSnap = () => {\r\n let { scroll, isHorizontal } = this.lenis\r\n scroll = Math.ceil(this.lenis.scroll)\r\n\r\n let snaps = [...this.snaps.values()] as SnapItem[]\r\n\r\n this.elements.forEach(({ rect, align }) => {\r\n let value: number | undefined\r\n\r\n align.forEach((align) => {\r\n if (align === 'start') {\r\n value = rect.top\r\n } else if (align === 'center') {\r\n value = isHorizontal\r\n ? rect.left + rect.width / 2 - this.viewport.width / 2\r\n : rect.top + rect.height / 2 - this.viewport.height / 2\r\n } else if (align === 'end') {\r\n value = isHorizontal\r\n ? rect.left + rect.width - this.viewport.width\r\n : rect.top + rect.height - this.viewport.height\r\n }\r\n\r\n if (typeof value === 'number') {\r\n snaps.push({ value: Math.ceil(value), userData: {} })\r\n }\r\n })\r\n })\r\n\r\n snaps = snaps.sort((a, b) => Math.abs(a.value) - Math.abs(b.value))\r\n\r\n if (snaps.length === 0) return\r\n\r\n let prevSnap = snaps.findLast(({ value }) => value <= scroll)\r\n if (prevSnap === undefined) prevSnap = snaps[0]!\r\n const distanceToPrevSnap = Math.abs(scroll - prevSnap.value)\r\n\r\n let nextSnap = snaps.find(({ value }) => value >= scroll)\r\n if (nextSnap === undefined) nextSnap = snaps[snaps.length - 1]!\r\n const distanceToNextSnap = Math.abs(scroll - nextSnap.value)\r\n\r\n const snap = distanceToPrevSnap < distanceToNextSnap ? prevSnap : nextSnap\r\n\r\n const distance = Math.abs(scroll - snap.value)\r\n\r\n let distanceThreshold\r\n\r\n const axis = isHorizontal ? 'width' : 'height'\r\n\r\n if (\r\n typeof this.options.distanceThreshold === 'string' &&\r\n this.options.distanceThreshold.endsWith('%')\r\n ) {\r\n distanceThreshold =\r\n (Number(this.options.distanceThreshold.replace('%', '')) / 100) *\r\n this.viewport[axis]\r\n } else if (typeof this.options.distanceThreshold === 'number') {\r\n distanceThreshold = this.options.distanceThreshold\r\n } else {\r\n distanceThreshold = this.viewport[axis]\r\n }\r\n\r\n if (\r\n this.options.type === 'mandatory' ||\r\n (this.options.type === 'proximity' && distance <= distanceThreshold)\r\n ) {\r\n // this.__isScrolling = true\r\n // this.onSnapStart?.(snap)\r\n\r\n // console.log('scroll to')\r\n\r\n this.lenis.scrollTo(snap.value, {\r\n lerp: this.options.lerp,\r\n easing: this.options.easing,\r\n duration: this.options.duration,\r\n userData: { initiator: 'snap' },\r\n onStart: () => {\r\n this.options.onSnapStart?.(snap)\r\n },\r\n onComplete: () => {\r\n this.options.onSnapComplete?.(snap)\r\n },\r\n })\r\n }\r\n\r\n // console.timeEnd('scroll')\r\n }\r\n}\r\n","// This file serves as an entry point for the package\r\nimport { Snap } from './src/snap'\r\nglobalThis.Snap = Snap\r\n"],"mappings":"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","names":["align"]}
|
|
1
|
+
{"version":3,"sources":["../packages/snap/src/debounce.ts","../packages/snap/src/element.ts","../packages/snap/src/uid.ts","../packages/snap/src/snap.ts","../packages/snap/browser.ts"],"sourcesContent":["export function debounce<CB extends (...args: any[]) => void>(\r\n callback: CB,\r\n delay: number\r\n) {\r\n let timer: number | undefined\r\n return function <T>(this: T, ...args: Parameters<typeof callback>) {\r\n let context = this\r\n clearTimeout(timer)\r\n timer = setTimeout(() => {\r\n timer = undefined\r\n callback.apply(context, args)\r\n }, delay)\r\n }\r\n}\r\n","function removeParentSticky(element: HTMLElement) {\r\n const position = getComputedStyle(element).position\r\n\r\n const isSticky = position === 'sticky'\r\n\r\n if (isSticky) {\r\n element.style.setProperty('position', 'static')\r\n element.dataset.sticky = 'true'\r\n }\r\n\r\n if (element.offsetParent) {\r\n removeParentSticky(element.offsetParent as HTMLElement)\r\n }\r\n}\r\n\r\nfunction addParentSticky(element: HTMLElement) {\r\n if (element?.dataset?.sticky === 'true') {\r\n element.style.removeProperty('position')\r\n delete element.dataset.sticky\r\n }\r\n\r\n if (element.offsetParent) {\r\n addParentSticky(element.offsetParent as HTMLElement)\r\n }\r\n}\r\n\r\nfunction offsetTop(element: HTMLElement, accumulator = 0) {\r\n const top = accumulator + element.offsetTop\r\n if (element.offsetParent) {\r\n return offsetTop(element.offsetParent as HTMLElement, top)\r\n }\r\n return top\r\n}\r\n\r\nfunction offsetLeft(element: HTMLElement, accumulator = 0) {\r\n const left = accumulator + element.offsetLeft\r\n if (element.offsetParent) {\r\n return offsetLeft(element.offsetParent as HTMLElement, left)\r\n }\r\n return left\r\n}\r\n\r\nfunction scrollTop(element: HTMLElement, accumulator = 0) {\r\n const top = accumulator + element.scrollTop\r\n if (element.offsetParent) {\r\n return scrollTop(element.offsetParent as HTMLElement, top)\r\n }\r\n return top + window.scrollY\r\n}\r\n\r\nfunction scrollLeft(element: HTMLElement, accumulator = 0) {\r\n const left = accumulator + element.scrollLeft\r\n if (element.offsetParent) {\r\n return scrollLeft(element.offsetParent as HTMLElement, left)\r\n }\r\n return left + window.scrollX\r\n}\r\n\r\nexport type SnapElementOptions = {\r\n align?: string | string[]\r\n ignoreSticky?: boolean\r\n ignoreTransform?: boolean\r\n}\r\n\r\ntype Rect = {\r\n top: number\r\n left: number\r\n width: number\r\n height: number\r\n x: number\r\n y: number\r\n bottom: number\r\n right: number\r\n element: HTMLElement\r\n}\r\n\r\nexport class SnapElement {\r\n element: HTMLElement\r\n options: SnapElementOptions\r\n align: string[]\r\n // @ts-ignore\r\n rect: Rect = {}\r\n wrapperResizeObserver: ResizeObserver\r\n resizeObserver: ResizeObserver\r\n\r\n constructor(\r\n element: HTMLElement,\r\n {\r\n align = ['start'],\r\n ignoreSticky = true,\r\n ignoreTransform = false,\r\n }: SnapElementOptions = {}\r\n ) {\r\n this.element = element\r\n\r\n this.options = { align, ignoreSticky, ignoreTransform }\r\n\r\n // this.ignoreSticky = ignoreSticky\r\n // this.ignoreTransform = ignoreTransform\r\n\r\n this.align = [align].flat()\r\n\r\n // TODO: assing rect immediately\r\n\r\n this.wrapperResizeObserver = new ResizeObserver(this.onWrapperResize)\r\n this.wrapperResizeObserver.observe(document.body)\r\n this.onWrapperResize()\r\n\r\n this.resizeObserver = new ResizeObserver(this.onResize)\r\n this.resizeObserver.observe(this.element)\r\n this.setRect({\r\n width: this.element.offsetWidth,\r\n height: this.element.offsetHeight,\r\n })\r\n }\r\n\r\n destroy() {\r\n this.wrapperResizeObserver.disconnect()\r\n this.resizeObserver.disconnect()\r\n }\r\n\r\n setRect({\r\n top,\r\n left,\r\n width,\r\n height,\r\n element,\r\n }: {\r\n top?: number\r\n left?: number\r\n width?: number\r\n height?: number\r\n element?: HTMLElement\r\n } = {}) {\r\n top = top ?? this.rect.top\r\n left = left ?? this.rect.left\r\n width = width ?? this.rect.width\r\n height = height ?? this.rect.height\r\n element = element ?? this.rect.element\r\n\r\n if (\r\n top === this.rect.top &&\r\n left === this.rect.left &&\r\n width === this.rect.width &&\r\n height === this.rect.height &&\r\n element === this.rect.element\r\n )\r\n return\r\n\r\n this.rect.top = top\r\n this.rect.y = top\r\n this.rect.width = width\r\n this.rect.height = height\r\n this.rect.left = left\r\n this.rect.x = left\r\n this.rect.bottom = top + height\r\n this.rect.right = left + width\r\n }\r\n\r\n onWrapperResize = () => {\r\n let top, left\r\n\r\n if (this.options.ignoreSticky) removeParentSticky(this.element)\r\n if (this.options.ignoreTransform) {\r\n top = offsetTop(this.element)\r\n left = offsetLeft(this.element)\r\n } else {\r\n const rect = this.element.getBoundingClientRect()\r\n top = rect.top + scrollTop(this.element)\r\n left = rect.left + scrollLeft(this.element)\r\n }\r\n if (this.options.ignoreSticky) addParentSticky(this.element)\r\n\r\n this.setRect({ top, left })\r\n }\r\n\r\n onResize = ([entry]: ResizeObserverEntry[]) => {\r\n if (!entry?.borderBoxSize[0]) return\r\n const width = entry.borderBoxSize[0].inlineSize\r\n const height = entry.borderBoxSize[0].blockSize\r\n\r\n this.setRect({ width, height })\r\n }\r\n}\r\n","let index = 0\r\n\r\nexport type UID = number\r\n\r\nexport function uid(): UID {\r\n return index++\r\n}\r\n","import type Lenis from 'lenis'\r\nimport { debounce } from './debounce'\r\nimport type { SnapElementOptions } from './element'\r\nimport { SnapElement } from './element'\r\nimport type { SnapItem, SnapOptions } from './types'\r\nimport type { UID } from './uid'\r\nimport { uid } from './uid'\r\n\r\n// TODO:\r\n// - horizontal\r\n// - fix trackpad snapping too soon due to velocity (fuck Apple)\r\n// - fix wheel scrolling after limits (see console scroll to)\r\n// - fix touch scroll, do not snap when not released\r\n// - arrow, spacebar\r\n\r\ntype RequiredPick<T, F extends keyof T> = Omit<T, F> & Required<Pick<T, F>>\r\n\r\n/**\r\n * Snap class to handle the snap functionality\r\n *\r\n * @example\r\n * const snap = new Snap(lenis, {\r\n * type: 'mandatory', // 'mandatory', 'proximity'\r\n * lerp: 0.1,\r\n * duration: 1,\r\n * easing: (t) => t,\r\n * onSnapStart: (snap) => {\r\n * console.log('onSnapStart', snap)\r\n * },\r\n * onSnapComplete: (snap) => {\r\n * console.log('onSnapComplete', snap)\r\n * },\r\n * })\r\n *\r\n * snap.add(500) // snap at 500px\r\n *\r\n * const removeSnap = snap.add(500)\r\n *\r\n * if (someCondition) {\r\n * removeSnap()\r\n * }\r\n */\r\nexport class Snap {\r\n options: RequiredPick<SnapOptions, 'type' | 'debounce'>\r\n elements = new Map<UID, SnapElement>()\r\n snaps = new Map<UID, SnapItem>()\r\n viewport: { width: number; height: number } = {\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n }\r\n isStopped = false\r\n onSnapDebounced?: () => void\r\n currentSnapIndex = 0 // used for slide type\r\n\r\n constructor(\r\n private lenis: Lenis,\r\n {\r\n type = 'proximity',\r\n lerp,\r\n easing,\r\n duration,\r\n distanceThreshold = '50%',\r\n debounce: debounceDelay = 500,\r\n onSnapStart,\r\n onSnapComplete,\r\n }: SnapOptions = {}\r\n ) {\r\n this.options = {\r\n type,\r\n lerp,\r\n easing,\r\n duration,\r\n distanceThreshold,\r\n debounce: debounceDelay,\r\n onSnapStart,\r\n onSnapComplete,\r\n }\r\n\r\n this.onWindowResize()\r\n window.addEventListener('resize', this.onWindowResize, false)\r\n\r\n if (this.options.type === 'slide') {\r\n this.lenis.on('scroll', this.onSlide)\r\n } else {\r\n this.onSnapDebounced = debounce(this.onSnap, this.options.debounce)\r\n\r\n // this.lenis.on('scroll', this.onScroll)\r\n this.lenis.on('virtual-scroll', this.onSnapDebounced)\r\n }\r\n }\r\n\r\n /**\r\n * Destroy the snap instance\r\n */\r\n destroy() {\r\n // this.lenis.off('scroll', this.onScroll)\r\n this.lenis.off('virtual-scroll', this.onSnapDebounced!)\r\n window.removeEventListener('resize', this.onWindowResize, false)\r\n this.elements.forEach((element) => element.destroy())\r\n }\r\n\r\n /**\r\n * Start the snap after it has been stopped\r\n */\r\n start() {\r\n this.isStopped = false\r\n }\r\n\r\n /**\r\n * Stop the snap\r\n */\r\n stop() {\r\n this.isStopped = true\r\n }\r\n\r\n /**\r\n * Add a snap to the snap instance\r\n *\r\n * @param value The value to snap to\r\n * @param userData User data that will be forwarded through the snap event\r\n * @returns Unsubscribe function\r\n */\r\n add(value: number) {\r\n const id = uid()\r\n\r\n this.snaps.set(id, { value })\r\n\r\n return () => this.snaps.delete(id)\r\n }\r\n\r\n /**\r\n * Add an element to the snap instance\r\n *\r\n * @param element The element to add\r\n * @param options The options for the element\r\n * @returns Unsubscribe function\r\n */\r\n addElement(element: HTMLElement, options: SnapElementOptions = {}) {\r\n const id = uid()\r\n\r\n this.elements.set(id, new SnapElement(element, options))\r\n\r\n return () => this.elements.delete(id)\r\n }\r\n\r\n private onWindowResize = () => {\r\n this.viewport.width = window.innerWidth\r\n this.viewport.height = window.innerHeight\r\n }\r\n\r\n private computeSnaps = () => {\r\n const { isHorizontal } = this.lenis\r\n\r\n let snaps = [...this.snaps.values()] as SnapItem[]\r\n\r\n this.elements.forEach(({ rect, align }) => {\r\n let value: number | undefined\r\n\r\n align.forEach((align) => {\r\n if (align === 'start') {\r\n value = rect.top\r\n } else if (align === 'center') {\r\n value = isHorizontal\r\n ? rect.left + rect.width / 2 - this.viewport.width / 2\r\n : rect.top + rect.height / 2 - this.viewport.height / 2\r\n } else if (align === 'end') {\r\n value = isHorizontal\r\n ? rect.left + rect.width - this.viewport.width\r\n : rect.top + rect.height - this.viewport.height\r\n }\r\n\r\n if (typeof value === 'number') {\r\n snaps.push({ value: Math.ceil(value) })\r\n }\r\n })\r\n })\r\n\r\n snaps = snaps.sort((a, b) => Math.abs(a.value) - Math.abs(b.value))\r\n\r\n return snaps\r\n }\r\n\r\n private onSlide = () => {\r\n const { direction, userData } = this.lenis\r\n\r\n if (userData?.initiator === 'snap') return\r\n\r\n const snaps = this.computeSnaps()\r\n\r\n if (snaps.length === 0) return\r\n\r\n if (direction === 1) {\r\n this.currentSnapIndex += 1\r\n } else if (direction === -1) {\r\n this.currentSnapIndex -= 1\r\n }\r\n\r\n this.currentSnapIndex = Math.max(\r\n 0,\r\n Math.min(this.currentSnapIndex, snaps.length - 1)\r\n )\r\n\r\n const currentSnap = snaps[this.currentSnapIndex]\r\n\r\n if (currentSnap === undefined) return\r\n\r\n this.lenis.scrollTo(currentSnap.value, {\r\n duration: this.options.duration,\r\n easing: this.options.easing,\r\n lerp: this.options.lerp,\r\n lock: true,\r\n userData: { initiator: 'snap' },\r\n onStart: () => {\r\n this.options.onSnapStart?.(currentSnap)\r\n },\r\n onComplete: () => {\r\n this.options.onSnapComplete?.(currentSnap)\r\n },\r\n })\r\n }\r\n\r\n private onSnap = () => {\r\n let { scroll, isHorizontal } = this.lenis\r\n scroll = Math.ceil(this.lenis.scroll)\r\n\r\n const snaps = this.computeSnaps()\r\n\r\n if (snaps.length === 0) return\r\n\r\n let prevSnap = snaps.findLast(({ value }) => value <= scroll)\r\n if (prevSnap === undefined) prevSnap = snaps[0]!\r\n const distanceToPrevSnap = Math.abs(scroll - prevSnap.value)\r\n\r\n let nextSnap = snaps.find(({ value }) => value >= scroll)\r\n if (nextSnap === undefined) nextSnap = snaps[snaps.length - 1]!\r\n const distanceToNextSnap = Math.abs(scroll - nextSnap.value)\r\n\r\n const snap = distanceToPrevSnap < distanceToNextSnap ? prevSnap : nextSnap\r\n\r\n const distance = Math.abs(scroll - snap.value)\r\n\r\n let distanceThreshold\r\n\r\n const axis = isHorizontal ? 'width' : 'height'\r\n\r\n if (\r\n typeof this.options.distanceThreshold === 'string' &&\r\n this.options.distanceThreshold.endsWith('%')\r\n ) {\r\n distanceThreshold =\r\n (Number(this.options.distanceThreshold.replace('%', '')) / 100) *\r\n this.viewport[axis]\r\n } else if (typeof this.options.distanceThreshold === 'number') {\r\n distanceThreshold = this.options.distanceThreshold\r\n } else {\r\n distanceThreshold = this.viewport[axis]\r\n }\r\n\r\n if (\r\n this.options.type === 'mandatory' ||\r\n (this.options.type === 'proximity' && distance <= distanceThreshold)\r\n ) {\r\n // this.__isScrolling = true\r\n // this.onSnapStart?.(snap)\r\n\r\n // console.log('scroll to')\r\n\r\n this.lenis.scrollTo(snap.value, {\r\n lerp: this.options.lerp,\r\n easing: this.options.easing,\r\n duration: this.options.duration,\r\n userData: { initiator: 'snap' },\r\n onStart: () => {\r\n this.options.onSnapStart?.(snap)\r\n },\r\n onComplete: () => {\r\n this.options.onSnapComplete?.(snap)\r\n },\r\n })\r\n }\r\n\r\n // console.timeEnd('scroll')\r\n }\r\n}\r\n","// This file serves as an entry point for the package\r\nimport { Snap } from './src/snap'\r\nglobalThis.Snap = Snap\r\n"],"mappings":"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","names":["align"]}
|
package/dist/lenis-snap.mjs
CHANGED
|
@@ -143,13 +143,13 @@ function uid() {
|
|
|
143
143
|
|
|
144
144
|
// packages/snap/src/snap.ts
|
|
145
145
|
var Snap = class {
|
|
146
|
+
// used for slide type
|
|
146
147
|
constructor(lenis, {
|
|
147
148
|
type = "proximity",
|
|
148
149
|
lerp,
|
|
149
150
|
easing,
|
|
150
151
|
duration,
|
|
151
152
|
distanceThreshold = "50%",
|
|
152
|
-
// velocityThreshold = 1.2,
|
|
153
153
|
debounce: debounceDelay = 500,
|
|
154
154
|
onSnapStart,
|
|
155
155
|
onSnapComplete
|
|
@@ -161,15 +161,18 @@ var Snap = class {
|
|
|
161
161
|
easing,
|
|
162
162
|
duration,
|
|
163
163
|
distanceThreshold,
|
|
164
|
-
// velocityThreshold,
|
|
165
164
|
debounce: debounceDelay,
|
|
166
165
|
onSnapStart,
|
|
167
166
|
onSnapComplete
|
|
168
167
|
};
|
|
169
168
|
this.onWindowResize();
|
|
170
169
|
window.addEventListener("resize", this.onWindowResize, false);
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
if (this.options.type === "slide") {
|
|
171
|
+
this.lenis.on("scroll", this.onSlide);
|
|
172
|
+
} else {
|
|
173
|
+
this.onSnapDebounced = debounce(this.onSnap, this.options.debounce);
|
|
174
|
+
this.lenis.on("virtual-scroll", this.onSnapDebounced);
|
|
175
|
+
}
|
|
173
176
|
}
|
|
174
177
|
options;
|
|
175
178
|
elements = /* @__PURE__ */ new Map();
|
|
@@ -180,6 +183,7 @@ var Snap = class {
|
|
|
180
183
|
};
|
|
181
184
|
isStopped = false;
|
|
182
185
|
onSnapDebounced;
|
|
186
|
+
currentSnapIndex = 0;
|
|
183
187
|
/**
|
|
184
188
|
* Destroy the snap instance
|
|
185
189
|
*/
|
|
@@ -207,9 +211,9 @@ var Snap = class {
|
|
|
207
211
|
* @param userData User data that will be forwarded through the snap event
|
|
208
212
|
* @returns Unsubscribe function
|
|
209
213
|
*/
|
|
210
|
-
add(value
|
|
214
|
+
add(value) {
|
|
211
215
|
const id = uid();
|
|
212
|
-
this.snaps.set(id, { value
|
|
216
|
+
this.snaps.set(id, { value });
|
|
213
217
|
return () => this.snaps.delete(id);
|
|
214
218
|
}
|
|
215
219
|
/**
|
|
@@ -228,33 +232,8 @@ var Snap = class {
|
|
|
228
232
|
this.viewport.width = window.innerWidth;
|
|
229
233
|
this.viewport.height = window.innerHeight;
|
|
230
234
|
};
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
// // limit,
|
|
234
|
-
// lastVelocity,
|
|
235
|
-
// velocity,
|
|
236
|
-
// // isScrolling,
|
|
237
|
-
// userData,
|
|
238
|
-
// }: // isHorizontal,
|
|
239
|
-
// Lenis) => {
|
|
240
|
-
// if (this.isStopped) return
|
|
241
|
-
// // return
|
|
242
|
-
// const isDecelerating = Math.abs(lastVelocity) > Math.abs(velocity)
|
|
243
|
-
// const isTurningBack =
|
|
244
|
-
// Math.sign(lastVelocity) !== Math.sign(velocity) && velocity !== 0
|
|
245
|
-
// if (
|
|
246
|
-
// Math.abs(velocity) < this.options.velocityThreshold &&
|
|
247
|
-
// // !isTouching &&
|
|
248
|
-
// isDecelerating &&
|
|
249
|
-
// !isTurningBack &&
|
|
250
|
-
// userData?.initiator !== 'snap'
|
|
251
|
-
// ) {
|
|
252
|
-
// this.onSnapDebounced()
|
|
253
|
-
// }
|
|
254
|
-
// }
|
|
255
|
-
onSnap = () => {
|
|
256
|
-
let { scroll, isHorizontal } = this.lenis;
|
|
257
|
-
scroll = Math.ceil(this.lenis.scroll);
|
|
235
|
+
computeSnaps = () => {
|
|
236
|
+
const { isHorizontal } = this.lenis;
|
|
258
237
|
let snaps = [...this.snaps.values()];
|
|
259
238
|
this.elements.forEach(({ rect, align }) => {
|
|
260
239
|
let value;
|
|
@@ -267,11 +246,47 @@ var Snap = class {
|
|
|
267
246
|
value = isHorizontal ? rect.left + rect.width - this.viewport.width : rect.top + rect.height - this.viewport.height;
|
|
268
247
|
}
|
|
269
248
|
if (typeof value === "number") {
|
|
270
|
-
snaps.push({ value: Math.ceil(value)
|
|
249
|
+
snaps.push({ value: Math.ceil(value) });
|
|
271
250
|
}
|
|
272
251
|
});
|
|
273
252
|
});
|
|
274
253
|
snaps = snaps.sort((a, b) => Math.abs(a.value) - Math.abs(b.value));
|
|
254
|
+
return snaps;
|
|
255
|
+
};
|
|
256
|
+
onSlide = () => {
|
|
257
|
+
const { direction, userData } = this.lenis;
|
|
258
|
+
if (userData?.initiator === "snap") return;
|
|
259
|
+
const snaps = this.computeSnaps();
|
|
260
|
+
if (snaps.length === 0) return;
|
|
261
|
+
if (direction === 1) {
|
|
262
|
+
this.currentSnapIndex += 1;
|
|
263
|
+
} else if (direction === -1) {
|
|
264
|
+
this.currentSnapIndex -= 1;
|
|
265
|
+
}
|
|
266
|
+
this.currentSnapIndex = Math.max(
|
|
267
|
+
0,
|
|
268
|
+
Math.min(this.currentSnapIndex, snaps.length - 1)
|
|
269
|
+
);
|
|
270
|
+
const currentSnap = snaps[this.currentSnapIndex];
|
|
271
|
+
if (currentSnap === void 0) return;
|
|
272
|
+
this.lenis.scrollTo(currentSnap.value, {
|
|
273
|
+
duration: this.options.duration,
|
|
274
|
+
easing: this.options.easing,
|
|
275
|
+
lerp: this.options.lerp,
|
|
276
|
+
lock: true,
|
|
277
|
+
userData: { initiator: "snap" },
|
|
278
|
+
onStart: () => {
|
|
279
|
+
this.options.onSnapStart?.(currentSnap);
|
|
280
|
+
},
|
|
281
|
+
onComplete: () => {
|
|
282
|
+
this.options.onSnapComplete?.(currentSnap);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
};
|
|
286
|
+
onSnap = () => {
|
|
287
|
+
let { scroll, isHorizontal } = this.lenis;
|
|
288
|
+
scroll = Math.ceil(this.lenis.scroll);
|
|
289
|
+
const snaps = this.computeSnaps();
|
|
275
290
|
if (snaps.length === 0) return;
|
|
276
291
|
let prevSnap = snaps.findLast(({ value }) => value <= scroll);
|
|
277
292
|
if (prevSnap === void 0) prevSnap = snaps[0];
|