Comparing version 1.1.5 to 1.1.6-dev.0
@@ -45,2 +45,6 @@ import Lenis from 'lenis'; | ||
}; | ||
type SnapItem = { | ||
value: number; | ||
userData: object; | ||
}; | ||
type SnapOptions = { | ||
@@ -52,4 +56,5 @@ type?: 'mandatory' | 'proximity'; | ||
velocityThreshold?: number; | ||
onSnapStart?: (t: number) => number; | ||
onSnapComplete?: (t: number) => number; | ||
debounce?: number; | ||
onSnapStart?: (t: SnapItem) => void; | ||
onSnapComplete?: (t: SnapItem) => void; | ||
}; | ||
@@ -60,25 +65,19 @@ declare class Snap { | ||
elements: Map<UID, SnapElement>; | ||
snaps: Map<UID, number>; | ||
snaps: Map<UID, SnapItem>; | ||
viewport: Viewport; | ||
isStopped: Boolean; | ||
constructor(lenis: Lenis, { type, lerp, easing, duration, velocityThreshold, onSnapStart, onSnapComplete, }?: SnapOptions); | ||
onSnapDebounced: Function; | ||
constructor(lenis: Lenis, { type, lerp, easing, duration, velocityThreshold, debounce: debounceDelay, onSnapStart, onSnapComplete, }?: SnapOptions); | ||
destroy(): void; | ||
start(): void; | ||
stop(): void; | ||
add(value: number): () => void; | ||
add(value: number, userData?: object): () => void; | ||
remove(id: UID): void; | ||
addElement(element: HTMLElement, options?: SnapElementOptions): () => void; | ||
removeElement(id: UID): void; | ||
onWindowResize: () => void; | ||
onScroll: ({ scroll, limit, lastVelocity, velocity, isScrolling, userData, isHorizontal, }: { | ||
scroll: any; | ||
limit: any; | ||
lastVelocity: any; | ||
velocity: any; | ||
isScrolling: any; | ||
userData: any; | ||
isHorizontal: any; | ||
}) => void; | ||
private onWindowResize; | ||
private onScroll; | ||
private onSnap; | ||
} | ||
export { type SnapOptions, Snap as default }; |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Lenis = factory()); | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Lenis = factory()); | ||
})(this, (function () { 'use strict'; | ||
function removeParentSticky(element) { | ||
const position = getComputedStyle(element).position; | ||
const isSticky = position === 'sticky'; | ||
if (isSticky) { | ||
element.style.setProperty('position', 'static'); | ||
element.dataset.sticky = 'true'; | ||
} | ||
if (element.offsetParent) { | ||
removeParentSticky(element.offsetParent); | ||
} | ||
function debounce(callback, delay) { | ||
let timer; | ||
return function () { | ||
let args = arguments; | ||
let context = this; | ||
clearTimeout(timer); | ||
timer = setTimeout(function () { | ||
callback.apply(context, args); | ||
}, delay); | ||
} | ||
function addParentSticky(element) { | ||
var _a; | ||
if (((_a = element === null || element === void 0 ? void 0 : element.dataset) === null || _a === void 0 ? void 0 : _a.sticky) === 'true') { | ||
element.style.removeProperty('position'); | ||
delete element.dataset.sticky; | ||
} | ||
if (element.offsetParent) { | ||
addParentSticky(element.offsetParent); | ||
} | ||
} | ||
function offsetTop(element, accumulator = 0) { | ||
const top = accumulator + element.offsetTop; | ||
if (element.offsetParent) { | ||
return offsetTop(element.offsetParent, top); | ||
} | ||
return top; | ||
} | ||
function offsetLeft(element, accumulator = 0) { | ||
const left = accumulator + element.offsetLeft; | ||
if (element.offsetParent) { | ||
return offsetLeft(element.offsetParent, left); | ||
} | ||
return left; | ||
} | ||
function scrollTop(element, accumulator = 0) { | ||
const top = accumulator + element.scrollTop; | ||
if (element.offsetParent) { | ||
return scrollTop(element.offsetParent, top); | ||
} | ||
return top + window.scrollY; | ||
} | ||
function scrollLeft(element, accumulator = 0) { | ||
const left = accumulator + element.scrollLeft; | ||
if (element.offsetParent) { | ||
return scrollLeft(element.offsetParent, left); | ||
} | ||
return left + window.scrollX; | ||
} | ||
class SnapElement { | ||
constructor(element, { align = ['start'], ignoreSticky = true, ignoreTransform = false, } = {}) { | ||
this.rect = {}; | ||
this.onWrapperResize = () => { | ||
let top, left; | ||
if (this.options.ignoreSticky) | ||
removeParentSticky(this.element); | ||
if (this.options.ignoreTransform) { | ||
top = offsetTop(this.element); | ||
left = offsetLeft(this.element); | ||
} | ||
else { | ||
const rect = this.element.getBoundingClientRect(); | ||
top = rect.top + scrollTop(this.element); | ||
left = rect.left + scrollLeft(this.element); | ||
} | ||
if (this.options.ignoreSticky) | ||
addParentSticky(this.element); | ||
this.setRect({ top, left }); | ||
}; | ||
this.onResize = ([entry]) => { | ||
const width = entry.borderBoxSize[0].inlineSize; | ||
const height = entry.borderBoxSize[0].blockSize; | ||
this.setRect({ width, height }); | ||
}; | ||
this.element = element; | ||
this.options = { align, ignoreSticky, ignoreTransform }; | ||
this.align = [align].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, left, width, height, element, } = {}) { | ||
top = top !== null && top !== void 0 ? top : this.rect.top; | ||
left = left !== null && left !== void 0 ? left : this.rect.left; | ||
width = width !== null && width !== void 0 ? width : this.rect.width; | ||
height = height !== null && height !== void 0 ? height : this.rect.height; | ||
element = element !== null && element !== void 0 ? element : this.rect.element; | ||
if (top === this.rect.top && | ||
left === this.rect.left && | ||
width === this.rect.width && | ||
height === this.rect.height && | ||
element === this.rect.element) | ||
return; | ||
this.rect.top = top; | ||
this.rect.y = top; | ||
this.rect.width = width; | ||
this.rect.height = height; | ||
this.rect.left = left; | ||
this.rect.x = left; | ||
this.rect.bottom = top + height; | ||
this.rect.right = left + width; | ||
} | ||
} | ||
} | ||
let index = 0; | ||
function uid() { | ||
return index++; | ||
} | ||
function removeParentSticky(element) { | ||
const position = getComputedStyle(element).position; | ||
const isSticky = position === 'sticky'; | ||
if (isSticky) { | ||
element.style.setProperty('position', 'static'); | ||
element.dataset.sticky = 'true'; | ||
} | ||
if (element.offsetParent) { | ||
removeParentSticky(element.offsetParent); | ||
} | ||
} | ||
function addParentSticky(element) { | ||
var _a; | ||
if (((_a = element === null || element === void 0 ? void 0 : element.dataset) === null || _a === void 0 ? void 0 : _a.sticky) === 'true') { | ||
element.style.removeProperty('position'); | ||
delete element.dataset.sticky; | ||
} | ||
if (element.offsetParent) { | ||
addParentSticky(element.offsetParent); | ||
} | ||
} | ||
function offsetTop(element, accumulator = 0) { | ||
const top = accumulator + element.offsetTop; | ||
if (element.offsetParent) { | ||
return offsetTop(element.offsetParent, top); | ||
} | ||
return top; | ||
} | ||
function offsetLeft(element, accumulator = 0) { | ||
const left = accumulator + element.offsetLeft; | ||
if (element.offsetParent) { | ||
return offsetLeft(element.offsetParent, left); | ||
} | ||
return left; | ||
} | ||
function scrollTop(element, accumulator = 0) { | ||
const top = accumulator + element.scrollTop; | ||
if (element.offsetParent) { | ||
return scrollTop(element.offsetParent, top); | ||
} | ||
return top + window.scrollY; | ||
} | ||
function scrollLeft(element, accumulator = 0) { | ||
const left = accumulator + element.scrollLeft; | ||
if (element.offsetParent) { | ||
return scrollLeft(element.offsetParent, left); | ||
} | ||
return left + window.scrollX; | ||
} | ||
class SnapElement { | ||
constructor(element, { align = ['start'], ignoreSticky = true, ignoreTransform = false, } = {}) { | ||
this.rect = {}; | ||
this.onWrapperResize = () => { | ||
let top, left; | ||
if (this.options.ignoreSticky) | ||
removeParentSticky(this.element); | ||
if (this.options.ignoreTransform) { | ||
top = offsetTop(this.element); | ||
left = offsetLeft(this.element); | ||
} | ||
else { | ||
const rect = this.element.getBoundingClientRect(); | ||
top = rect.top + scrollTop(this.element); | ||
left = rect.left + scrollLeft(this.element); | ||
} | ||
if (this.options.ignoreSticky) | ||
addParentSticky(this.element); | ||
this.setRect({ top, left }); | ||
}; | ||
this.onResize = ([entry]) => { | ||
const width = entry.borderBoxSize[0].inlineSize; | ||
const height = entry.borderBoxSize[0].blockSize; | ||
this.setRect({ width, height }); | ||
}; | ||
this.element = element; | ||
this.options = { align, ignoreSticky, ignoreTransform }; | ||
this.align = [align].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, left, width, height, element, } = {}) { | ||
top = top !== null && top !== void 0 ? top : this.rect.top; | ||
left = left !== null && left !== void 0 ? left : this.rect.left; | ||
width = width !== null && width !== void 0 ? width : this.rect.width; | ||
height = height !== null && height !== void 0 ? height : this.rect.height; | ||
element = element !== null && element !== void 0 ? element : this.rect.element; | ||
if (top === this.rect.top && | ||
left === this.rect.left && | ||
width === this.rect.width && | ||
height === this.rect.height && | ||
element === this.rect.element) | ||
return; | ||
this.rect.top = top; | ||
this.rect.y = top; | ||
this.rect.width = width; | ||
this.rect.height = height; | ||
this.rect.left = left; | ||
this.rect.x = left; | ||
this.rect.bottom = top + height; | ||
this.rect.right = left + width; | ||
} | ||
} | ||
class Snap { | ||
constructor(lenis, { type = 'mandatory', lerp, easing, duration, velocityThreshold = 1, onSnapStart, onSnapComplete, } = {}) { | ||
this.isStopped = false; | ||
this.onWindowResize = () => { | ||
this.viewport.width = window.innerWidth; | ||
this.viewport.height = window.innerHeight; | ||
}; | ||
this.onScroll = ({ scroll, limit, lastVelocity, velocity, isScrolling, userData, isHorizontal, }) => { | ||
if (this.isStopped) | ||
return; | ||
const isDecelerating = Math.abs(lastVelocity) > Math.abs(velocity); | ||
const isTurningBack = Math.sign(lastVelocity) !== Math.sign(velocity) && velocity !== 0; | ||
if (Math.abs(velocity) < this.options.velocityThreshold && | ||
isDecelerating && | ||
!isTurningBack && | ||
(userData === null || userData === void 0 ? void 0 : userData.initiator) !== 'snap') { | ||
scroll = Math.ceil(scroll); | ||
let snaps = [...this.snaps.values()]; | ||
this.elements.forEach(({ rect, align }) => { | ||
let snap; | ||
align.forEach((align) => { | ||
if (align === 'start') { | ||
snap = rect.top; | ||
} | ||
else if (align === 'center') { | ||
snap = isHorizontal | ||
? rect.left + rect.width / 2 - this.viewport.width / 2 | ||
: rect.top + rect.height / 2 - this.viewport.height / 2; | ||
} | ||
else if (align === 'end') { | ||
snap = isHorizontal | ||
? rect.left + rect.width - this.viewport.width | ||
: rect.top + rect.height - this.viewport.height; | ||
} | ||
if (snap !== undefined) { | ||
snaps.push(Math.ceil(snap)); | ||
} | ||
}); | ||
}); | ||
snaps = snaps.sort((a, b) => Math.abs(a) - Math.abs(b)); | ||
let prevSnap = snaps.findLast((snap) => snap <= scroll); | ||
if (prevSnap === undefined) | ||
prevSnap = snaps[0]; | ||
const distanceToPrevSnap = Math.abs(scroll - prevSnap); | ||
let nextSnap = snaps.find((snap) => snap >= scroll); | ||
if (nextSnap === undefined) | ||
nextSnap = snaps[snaps.length - 1]; | ||
const distanceToNextSnap = Math.abs(scroll - nextSnap); | ||
const snap = distanceToPrevSnap < distanceToNextSnap ? prevSnap : nextSnap; | ||
const distance = Math.abs(scroll - snap); | ||
if (this.options.type === 'mandatory' || | ||
(this.options.type === 'proximity' && distance <= this.viewport.height)) { | ||
this.lenis.scrollTo(snap, { | ||
lerp: this.options.lerp, | ||
easing: this.options.easing, | ||
duration: this.options.duration, | ||
userData: { initiator: 'snap' }, | ||
onStart: () => { | ||
var _a, _b; | ||
(_b = (_a = this.options).onSnapStart) === null || _b === void 0 ? void 0 : _b.call(_a, snap); | ||
}, | ||
onComplete: () => { | ||
var _a, _b; | ||
(_b = (_a = this.options).onSnapComplete) === null || _b === void 0 ? void 0 : _b.call(_a, snap); | ||
}, | ||
}); | ||
} | ||
} | ||
}; | ||
this.lenis = lenis; | ||
this.options = { | ||
type, | ||
lerp, | ||
easing, | ||
duration, | ||
velocityThreshold, | ||
onSnapStart, | ||
onSnapComplete, | ||
}; | ||
this.elements = new Map(); | ||
this.snaps = new Map(); | ||
this.viewport = { | ||
width: window.innerWidth, | ||
height: window.innerHeight, | ||
}; | ||
this.onWindowResize(); | ||
window.addEventListener('resize', this.onWindowResize); | ||
this.lenis.on('scroll', this.onScroll); | ||
} | ||
destroy() { | ||
this.lenis.off('scroll', this.onScroll); | ||
window.removeEventListener('resize', this.onWindowResize); | ||
this.elements.forEach((element) => element.destroy()); | ||
} | ||
start() { | ||
this.isStopped = false; | ||
} | ||
stop() { | ||
this.isStopped = true; | ||
} | ||
add(value) { | ||
const id = uid(); | ||
this.snaps.set(id, value); | ||
return () => this.remove(id); | ||
} | ||
remove(id) { | ||
this.snaps.delete(id); | ||
} | ||
addElement(element, options = {}) { | ||
const id = uid(); | ||
this.elements.set(id, new SnapElement(element, options)); | ||
return () => this.removeElement(id); | ||
} | ||
removeElement(id) { | ||
this.elements.delete(id); | ||
} | ||
} | ||
let index = 0; | ||
function uid() { | ||
return index++; | ||
} | ||
return Snap; | ||
class Snap { | ||
constructor(lenis, { type = 'mandatory', lerp, easing, duration, velocityThreshold = 1, debounce: debounceDelay = 0, onSnapStart, onSnapComplete, } = {}) { | ||
this.isStopped = false; | ||
this.onWindowResize = () => { | ||
this.viewport.width = window.innerWidth; | ||
this.viewport.height = window.innerHeight; | ||
}; | ||
this.onScroll = ({ lastVelocity, velocity, userData, }) => { | ||
if (this.isStopped) | ||
return; | ||
const isDecelerating = Math.abs(lastVelocity) > Math.abs(velocity); | ||
const isTurningBack = Math.sign(lastVelocity) !== Math.sign(velocity) && velocity !== 0; | ||
if (Math.abs(velocity) < this.options.velocityThreshold && | ||
isDecelerating && | ||
!isTurningBack && | ||
(userData === null || userData === void 0 ? void 0 : userData.initiator) !== 'snap') { | ||
this.onSnapDebounced(); | ||
} | ||
}; | ||
this.onSnap = () => { | ||
let { scroll, isHorizontal } = this.lenis; | ||
scroll = Math.ceil(this.lenis.scroll); | ||
let snaps = [...this.snaps.values()]; | ||
this.elements.forEach(({ rect, align }) => { | ||
let value; | ||
align.forEach((align) => { | ||
if (align === 'start') { | ||
value = rect.top; | ||
} | ||
else if (align === 'center') { | ||
value = isHorizontal | ||
? rect.left + rect.width / 2 - this.viewport.width / 2 | ||
: rect.top + rect.height / 2 - this.viewport.height / 2; | ||
} | ||
else if (align === 'end') { | ||
value = isHorizontal | ||
? rect.left + rect.width - this.viewport.width | ||
: rect.top + rect.height - this.viewport.height; | ||
} | ||
if (typeof value === 'number') { | ||
snaps.push({ value: Math.ceil(value), userData: {} }); | ||
} | ||
}); | ||
}); | ||
snaps = snaps.sort((a, b) => Math.abs(a.value) - Math.abs(b.value)); | ||
let prevSnap = snaps.findLast(({ value }) => value <= scroll); | ||
if (prevSnap === undefined) | ||
prevSnap = snaps[0]; | ||
const distanceToPrevSnap = Math.abs(scroll - prevSnap.value); | ||
let nextSnap = snaps.find(({ value }) => value >= scroll); | ||
if (nextSnap === undefined) | ||
nextSnap = snaps[snaps.length - 1]; | ||
const distanceToNextSnap = Math.abs(scroll - nextSnap.value); | ||
const snap = distanceToPrevSnap < distanceToNextSnap ? prevSnap : nextSnap; | ||
const distance = Math.abs(scroll - snap.value); | ||
if (this.options.type === 'mandatory' || | ||
(this.options.type === 'proximity' && | ||
distance <= this.lenis.dimensions.height)) { | ||
this.lenis.scrollTo(snap.value, { | ||
lerp: this.options.lerp, | ||
easing: this.options.easing, | ||
duration: this.options.duration, | ||
userData: { initiator: 'snap' }, | ||
onStart: () => { | ||
var _a, _b; | ||
(_b = (_a = this.options).onSnapStart) === null || _b === void 0 ? void 0 : _b.call(_a, snap); | ||
}, | ||
onComplete: () => { | ||
var _a, _b; | ||
(_b = (_a = this.options).onSnapComplete) === null || _b === void 0 ? void 0 : _b.call(_a, snap); | ||
}, | ||
}); | ||
} | ||
}; | ||
this.lenis = lenis; | ||
this.options = { | ||
type, | ||
lerp, | ||
easing, | ||
duration, | ||
velocityThreshold, | ||
debounce: debounceDelay, | ||
onSnapStart, | ||
onSnapComplete, | ||
}; | ||
this.elements = new Map(); | ||
this.snaps = new Map(); | ||
this.viewport = { | ||
width: window.innerWidth, | ||
height: window.innerHeight, | ||
}; | ||
this.onWindowResize(); | ||
window.addEventListener('resize', this.onWindowResize, false); | ||
this.onSnapDebounced = debounce(this.onSnap, this.options.debounce); | ||
this.lenis.on('scroll', this.onScroll); | ||
} | ||
destroy() { | ||
this.lenis.off('scroll', this.onScroll); | ||
window.removeEventListener('resize', this.onWindowResize, false); | ||
this.elements.forEach((element) => element.destroy()); | ||
} | ||
start() { | ||
this.isStopped = false; | ||
} | ||
stop() { | ||
this.isStopped = true; | ||
} | ||
add(value, userData = {}) { | ||
const id = uid(); | ||
this.snaps.set(id, { value, userData }); | ||
return () => this.remove(id); | ||
} | ||
remove(id) { | ||
this.snaps.delete(id); | ||
} | ||
addElement(element, options = {}) { | ||
const id = uid(); | ||
this.elements.set(id, new SnapElement(element, options)); | ||
return () => this.removeElement(id); | ||
} | ||
removeElement(id) { | ||
this.elements.delete(id); | ||
} | ||
} | ||
return Snap; | ||
})); | ||
//# sourceMappingURL=lenis-snap.js.map |
@@ -1,2 +0,2 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Lenis=e()}(this,(function(){"use strict";function removeParentSticky(t){"sticky"===getComputedStyle(t).position&&(t.style.setProperty("position","static"),t.dataset.sticky="true"),t.offsetParent&&removeParentSticky(t.offsetParent)}function addParentSticky(t){var e;"true"===(null===(e=null==t?void 0:t.dataset)||void 0===e?void 0:e.sticky)&&(t.style.removeProperty("position"),delete t.dataset.sticky),t.offsetParent&&addParentSticky(t.offsetParent)}function offsetTop(t,e=0){const i=e+t.offsetTop;return t.offsetParent?offsetTop(t.offsetParent,i):i}function offsetLeft(t,e=0){const i=e+t.offsetLeft;return t.offsetParent?offsetLeft(t.offsetParent,i):i}function scrollTop(t,e=0){const i=e+t.scrollTop;return t.offsetParent?scrollTop(t.offsetParent,i):i+window.scrollY}function scrollLeft(t,e=0){const i=e+t.scrollLeft;return t.offsetParent?scrollLeft(t.offsetParent,i):i+window.scrollX}class SnapElement{constructor(t,{align:e=["start"],ignoreSticky:i=!0,ignoreTransform:s=!1}={}){this.rect={},this.onWrapperResize=()=>{let t,e;if(this.options.ignoreSticky&&removeParentSticky(this.element),this.options.ignoreTransform)t=offsetTop(this.element),e=offsetLeft(this.element);else{const i=this.element.getBoundingClientRect();t=i.top+scrollTop(this.element),e=i.left+scrollLeft(this.element)}this.options.ignoreSticky&&addParentSticky(this.element),this.setRect({top:t,left:e})},this.onResize=([t])=>{const e=t.borderBoxSize[0].inlineSize,i=t.borderBoxSize[0].blockSize;this.setRect({width:e,height:i})},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:o}={}){t=null!=t?t:this.rect.top,e=null!=e?e:this.rect.left,i=null!=i?i:this.rect.width,s=null!=s?s:this.rect.height,o=null!=o?o:this.rect.element,t===this.rect.top&&e===this.rect.left&&i===this.rect.width&&s===this.rect.height&&o===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)}}let t=0;function uid(){return t++}return class Snap{constructor(t,{type:e="mandatory",lerp:i,easing:s,duration:o,velocityThreshold:n=1,onSnapStart:r,onSnapComplete:h}={}){this.isStopped=!1,this.onWindowResize=()=>{this.viewport.width=window.innerWidth,this.viewport.height=window.innerHeight},this.onScroll=({scroll:t,limit:e,lastVelocity:i,velocity:s,isScrolling:o,userData:n,isHorizontal:r})=>{if(this.isStopped)return;const h=Math.abs(i)>Math.abs(s),l=Math.sign(i)!==Math.sign(s)&&0!==s;if(Math.abs(s)<this.options.velocityThreshold&&h&&!l&&"snap"!==(null==n?void 0:n.initiator)){t=Math.ceil(t);let e=[...this.snaps.values()];this.elements.forEach((({rect:t,align:i})=>{let s;i.forEach((i=>{"start"===i?s=t.top:"center"===i?s=r?t.left+t.width/2-this.viewport.width/2:t.top+t.height/2-this.viewport.height/2:"end"===i&&(s=r?t.left+t.width-this.viewport.width:t.top+t.height-this.viewport.height),void 0!==s&&e.push(Math.ceil(s))}))})),e=e.sort(((t,e)=>Math.abs(t)-Math.abs(e)));let i=e.findLast((e=>e<=t));void 0===i&&(i=e[0]);const s=Math.abs(t-i);let o=e.find((e=>e>=t));void 0===o&&(o=e[e.length-1]);const n=s<Math.abs(t-o)?i:o,h=Math.abs(t-n);("mandatory"===this.options.type||"proximity"===this.options.type&&h<=this.viewport.height)&&this.lenis.scrollTo(n,{lerp:this.options.lerp,easing:this.options.easing,duration:this.options.duration,userData:{initiator:"snap"},onStart:()=>{var t,e;null===(e=(t=this.options).onSnapStart)||void 0===e||e.call(t,n)},onComplete:()=>{var t,e;null===(e=(t=this.options).onSnapComplete)||void 0===e||e.call(t,n)}})}},this.lenis=t,this.options={type:e,lerp:i,easing:s,duration:o,velocityThreshold:n,onSnapStart:r,onSnapComplete:h},this.elements=new Map,this.snaps=new Map,this.viewport={width:window.innerWidth,height:window.innerHeight},this.onWindowResize(),window.addEventListener("resize",this.onWindowResize),this.lenis.on("scroll",this.onScroll)}destroy(){this.lenis.off("scroll",this.onScroll),window.removeEventListener("resize",this.onWindowResize),this.elements.forEach((t=>t.destroy()))}start(){this.isStopped=!1}stop(){this.isStopped=!0}add(t){const e=uid();return this.snaps.set(e,t),()=>this.remove(e)}remove(t){this.snaps.delete(t)}addElement(t,e={}){const i=uid();return this.elements.set(i,new SnapElement(t,e)),()=>this.removeElement(i)}removeElement(t){this.elements.delete(t)}}})); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Lenis=e()}(this,(function(){"use strict";function removeParentSticky(t){"sticky"===getComputedStyle(t).position&&(t.style.setProperty("position","static"),t.dataset.sticky="true"),t.offsetParent&&removeParentSticky(t.offsetParent)}function addParentSticky(t){var e;"true"===(null===(e=null==t?void 0:t.dataset)||void 0===e?void 0:e.sticky)&&(t.style.removeProperty("position"),delete t.dataset.sticky),t.offsetParent&&addParentSticky(t.offsetParent)}function offsetTop(t,e=0){const i=e+t.offsetTop;return t.offsetParent?offsetTop(t.offsetParent,i):i}function offsetLeft(t,e=0){const i=e+t.offsetLeft;return t.offsetParent?offsetLeft(t.offsetParent,i):i}function scrollTop(t,e=0){const i=e+t.scrollTop;return t.offsetParent?scrollTop(t.offsetParent,i):i+window.scrollY}function scrollLeft(t,e=0){const i=e+t.scrollLeft;return t.offsetParent?scrollLeft(t.offsetParent,i):i+window.scrollX}class SnapElement{constructor(t,{align:e=["start"],ignoreSticky:i=!0,ignoreTransform:s=!1}={}){this.rect={},this.onWrapperResize=()=>{let t,e;if(this.options.ignoreSticky&&removeParentSticky(this.element),this.options.ignoreTransform)t=offsetTop(this.element),e=offsetLeft(this.element);else{const i=this.element.getBoundingClientRect();t=i.top+scrollTop(this.element),e=i.left+scrollLeft(this.element)}this.options.ignoreSticky&&addParentSticky(this.element),this.setRect({top:t,left:e})},this.onResize=([t])=>{const e=t.borderBoxSize[0].inlineSize,i=t.borderBoxSize[0].blockSize;this.setRect({width:e,height:i})},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:o}={}){t=null!=t?t:this.rect.top,e=null!=e?e:this.rect.left,i=null!=i?i:this.rect.width,s=null!=s?s:this.rect.height,o=null!=o?o:this.rect.element,t===this.rect.top&&e===this.rect.left&&i===this.rect.width&&s===this.rect.height&&o===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)}}let t=0;function uid(){return t++}return class Snap{constructor(t,{type:e="mandatory",lerp:i,easing:s,duration:o,velocityThreshold:n=1,debounce:r=0,onSnapStart:h,onSnapComplete:l}={}){this.isStopped=!1,this.onWindowResize=()=>{this.viewport.width=window.innerWidth,this.viewport.height=window.innerHeight},this.onScroll=({lastVelocity:t,velocity:e,userData:i})=>{if(this.isStopped)return;const s=Math.abs(t)>Math.abs(e),o=Math.sign(t)!==Math.sign(e)&&0!==e;Math.abs(e)<this.options.velocityThreshold&&s&&!o&&"snap"!==(null==i?void 0:i.initiator)&&this.onSnapDebounced()},this.onSnap=()=>{let{scroll:t,isHorizontal:e}=this.lenis;t=Math.ceil(this.lenis.scroll);let i=[...this.snaps.values()];this.elements.forEach((({rect:t,align:s})=>{let o;s.forEach((s=>{"start"===s?o=t.top:"center"===s?o=e?t.left+t.width/2-this.viewport.width/2:t.top+t.height/2-this.viewport.height/2:"end"===s&&(o=e?t.left+t.width-this.viewport.width:t.top+t.height-this.viewport.height),"number"==typeof o&&i.push({value:Math.ceil(o),userData:{}})}))})),i=i.sort(((t,e)=>Math.abs(t.value)-Math.abs(e.value)));let s=i.findLast((({value:e})=>e<=t));void 0===s&&(s=i[0]);const o=Math.abs(t-s.value);let n=i.find((({value:e})=>e>=t));void 0===n&&(n=i[i.length-1]);const r=o<Math.abs(t-n.value)?s:n,h=Math.abs(t-r.value);("mandatory"===this.options.type||"proximity"===this.options.type&&h<=this.lenis.dimensions.height)&&this.lenis.scrollTo(r.value,{lerp:this.options.lerp,easing:this.options.easing,duration:this.options.duration,userData:{initiator:"snap"},onStart:()=>{var t,e;null===(e=(t=this.options).onSnapStart)||void 0===e||e.call(t,r)},onComplete:()=>{var t,e;null===(e=(t=this.options).onSnapComplete)||void 0===e||e.call(t,r)}})},this.lenis=t,this.options={type:e,lerp:i,easing:s,duration:o,velocityThreshold:n,debounce:r,onSnapStart:h,onSnapComplete:l},this.elements=new Map,this.snaps=new Map,this.viewport={width:window.innerWidth,height:window.innerHeight},this.onWindowResize(),window.addEventListener("resize",this.onWindowResize,!1),this.onSnapDebounced=function debounce(t,e){let i;return function(){let s=arguments,o=this;clearTimeout(i),i=setTimeout((function(){t.apply(o,s)}),e)}}(this.onSnap,this.options.debounce),this.lenis.on("scroll",this.onScroll)}destroy(){this.lenis.off("scroll",this.onScroll),window.removeEventListener("resize",this.onWindowResize,!1),this.elements.forEach((t=>t.destroy()))}start(){this.isStopped=!1}stop(){this.isStopped=!0}add(t,e={}){const i=uid();return this.snaps.set(i,{value:t,userData:e}),()=>this.remove(i)}remove(t){this.snaps.delete(t)}addElement(t,e={}){const i=uid();return this.elements.set(i,new SnapElement(t,e)),()=>this.removeElement(i)}removeElement(t){this.elements.delete(t)}}})); | ||
//# sourceMappingURL=lenis-snap.min.js.map |
@@ -0,1 +1,87 @@ | ||
declare class Animate { | ||
isRunning: boolean; | ||
value: number; | ||
from: number; | ||
to: number; | ||
lerp?: number; | ||
duration?: number; | ||
easing?: Function; | ||
currentTime: number; | ||
onUpdate?: Function; | ||
advance(deltaTime: number): void; | ||
stop(): void; | ||
fromTo(from: number, to: number, { lerp, duration, easing, onStart, onUpdate, }: { | ||
lerp?: number; | ||
duration?: number; | ||
easing?: Function; | ||
onStart?: Function; | ||
onUpdate?: Function; | ||
}): void; | ||
} | ||
type DimensionsOptions = { | ||
wrapper: Window | HTMLElement; | ||
content: HTMLElement; | ||
autoResize?: boolean; | ||
debounce?: number; | ||
}; | ||
declare class Dimensions { | ||
wrapper: Window | HTMLElement; | ||
content: HTMLElement; | ||
width: number; | ||
height: number; | ||
scrollWidth: number; | ||
scrollHeight: number; | ||
debouncedResize?: Function; | ||
wrapperResizeObserver?: ResizeObserver; | ||
contentResizeObserver?: ResizeObserver; | ||
constructor({ wrapper, content, autoResize, debounce: debounceValue, }?: DimensionsOptions); | ||
destroy(): void; | ||
resize: () => void; | ||
onWrapperResize: () => void; | ||
onContentResize: () => void; | ||
get limit(): { | ||
x: number; | ||
y: number; | ||
}; | ||
} | ||
declare class Emitter { | ||
events: Record<string, Function[]>; | ||
constructor(); | ||
emit(event: string, ...args: any[]): void; | ||
on(event: string, callback: Function): () => void; | ||
off(event: string, callback: Function): void; | ||
destroy(): void; | ||
} | ||
declare class VirtualScroll { | ||
element: HTMLElement | Window; | ||
wheelMultiplier: number; | ||
touchMultiplier: number; | ||
touchStart: { | ||
x: number | null; | ||
y: number | null; | ||
}; | ||
emitter: Emitter; | ||
lastDelta: { | ||
x: number; | ||
y: number; | ||
}; | ||
windowWidth: number; | ||
windowHeight: number; | ||
constructor(element: HTMLElement | Window, { wheelMultiplier, touchMultiplier }: { | ||
wheelMultiplier?: number | undefined; | ||
touchMultiplier?: number | undefined; | ||
}); | ||
on(event: string, callback: Function): () => void; | ||
destroy(): void; | ||
onTouchStart: (event: TouchEvent) => void; | ||
onTouchMove: (event: TouchEvent) => void; | ||
onTouchEnd: (event: TouchEvent) => void; | ||
onWheel: (event: WheelEvent) => void; | ||
onWindowResize: () => void; | ||
} | ||
type Overwrite<T, R> = Omit<T, keyof R> & R; | ||
type EasingFunction = (t: number) => number; | ||
@@ -5,2 +91,7 @@ type Orientation = 'vertical' | 'horizontal'; | ||
type Scrolling = boolean | 'native' | 'smooth'; | ||
type onVirtualScrollOptions = { | ||
deltaX: number; | ||
deltaY: number; | ||
event: WheelEvent | TouchEvent; | ||
}; | ||
type LenisOptions = Partial<{ | ||
@@ -24,3 +115,4 @@ wrapper: Window | HTMLElement; | ||
autoResize: boolean; | ||
prevent: boolean | ((node: Element) => boolean); | ||
prevent: (node: Element) => boolean; | ||
virtualScroll: (data: onVirtualScrollOptions) => boolean; | ||
__experimental__naiveDimensions: boolean; | ||
@@ -34,14 +126,21 @@ }>; | ||
__resetVelocityTimeout?: number; | ||
isTouching?: boolean; | ||
time: number; | ||
userData: object; | ||
userData: Object; | ||
lastVelocity: number; | ||
velocity: number; | ||
direction: 1 | -1 | 0; | ||
options: LenisOptions; | ||
options: Overwrite<LenisOptions, { | ||
wrapper: NonNullable<LenisOptions['wrapper']>; | ||
}>; | ||
targetScroll: number; | ||
animatedScroll: number; | ||
constructor({ wrapper, content, wheelEventsTarget, eventsTarget, smoothWheel, syncTouch, syncTouchLerp, touchInertiaMultiplier, duration, easing, lerp, infinite, orientation, gestureOrientation, touchMultiplier, wheelMultiplier, autoResize, prevent, __experimental__naiveDimensions, }?: LenisOptions); | ||
animate: Animate; | ||
emitter: Emitter; | ||
dimensions: Dimensions; | ||
virtualScroll: VirtualScroll; | ||
constructor({ wrapper, content, wheelEventsTarget, eventsTarget, smoothWheel, syncTouch, syncTouchLerp, touchInertiaMultiplier, duration, easing, lerp, infinite, orientation, gestureOrientation, touchMultiplier, wheelMultiplier, autoResize, prevent, virtualScroll, __experimental__naiveDimensions, }?: LenisOptions); | ||
destroy(): void; | ||
on(event: string, callback: Function): any; | ||
off(event: string, callback: Function): any; | ||
on(event: string, callback: Function): () => void; | ||
off(event: string, callback: Function): void; | ||
private setScroll; | ||
@@ -72,3 +171,3 @@ private onPointerDown; | ||
get rootElement(): HTMLElement; | ||
get limit(): any; | ||
get limit(): number; | ||
get isHorizontal(): boolean; | ||
@@ -75,0 +174,0 @@ get actualScroll(): number; |
@@ -7,3 +7,3 @@ (function (global, factory) { | ||
var version = "1.1.5"; | ||
var version = "1.1.6-dev.0"; | ||
@@ -31,56 +31,53 @@ // Clamp a value between a minimum and maximum value | ||
// Animate class to handle value animations with lerping or easing | ||
class Animate { | ||
// Advance the animation by the given delta time | ||
advance(deltaTime) { | ||
if (!this.isRunning) return | ||
let completed = false; | ||
if (this.duration && this.easing) { | ||
this.currentTime += deltaTime; | ||
const linearProgress = clamp(0, this.currentTime / this.duration, 1); | ||
completed = linearProgress >= 1; | ||
const easedProgress = completed ? 1 : this.easing(linearProgress); | ||
this.value = this.from + (this.to - this.from) * easedProgress; | ||
} else if (this.lerp) { | ||
this.value = damp(this.value, this.to, this.lerp * 60, deltaTime); | ||
if (Math.round(this.value) === this.to) { | ||
this.value = this.to; | ||
completed = true; | ||
} | ||
} else { | ||
// If no easing or lerp, just jump to the end value | ||
this.value = this.to; | ||
completed = true; | ||
constructor() { | ||
this.isRunning = false; | ||
this.value = 0; | ||
this.from = 0; | ||
this.to = 0; | ||
this.duration = 0; | ||
this.currentTime = 0; | ||
} | ||
if (completed) { | ||
this.stop(); | ||
advance(deltaTime) { | ||
var _a; | ||
if (!this.isRunning) | ||
return; | ||
let completed = false; | ||
if (this.duration && this.easing) { | ||
this.currentTime += deltaTime; | ||
const linearProgress = clamp(0, this.currentTime / this.duration, 1); | ||
completed = linearProgress >= 1; | ||
const easedProgress = completed ? 1 : this.easing(linearProgress); | ||
this.value = this.from + (this.to - this.from) * easedProgress; | ||
} | ||
else if (this.lerp) { | ||
this.value = damp(this.value, this.to, this.lerp * 60, deltaTime); | ||
if (Math.round(this.value) === this.to) { | ||
this.value = this.to; | ||
completed = true; | ||
} | ||
} | ||
else { | ||
this.value = this.to; | ||
completed = true; | ||
} | ||
if (completed) { | ||
this.stop(); | ||
} | ||
(_a = this.onUpdate) === null || _a === void 0 ? void 0 : _a.call(this, this.value, completed); | ||
} | ||
// Call the onUpdate callback with the current value and completed status | ||
this.onUpdate?.(this.value, completed); | ||
} | ||
// Stop the animation | ||
stop() { | ||
this.isRunning = false; | ||
} | ||
// Set up the animation from a starting value to an ending value | ||
// with optional parameters for lerping, duration, easing, and onUpdate callback | ||
fromTo(from, to, { lerp, duration, easing, onStart, onUpdate }) { | ||
this.from = this.value = from; | ||
this.to = to; | ||
this.lerp = lerp; | ||
this.duration = duration; | ||
this.easing = easing; | ||
this.currentTime = 0; | ||
this.isRunning = true; | ||
onStart?.(); | ||
this.onUpdate = onUpdate; | ||
} | ||
stop() { | ||
this.isRunning = false; | ||
} | ||
fromTo(from, to, { lerp, duration, easing, onStart, onUpdate, }) { | ||
this.from = this.value = from; | ||
this.to = to; | ||
this.lerp = lerp; | ||
this.duration = duration; | ||
this.easing = easing; | ||
this.currentTime = 0; | ||
this.isRunning = true; | ||
onStart === null || onStart === void 0 ? void 0 : onStart(); | ||
this.onUpdate = onUpdate; | ||
} | ||
} | ||
@@ -101,234 +98,197 @@ | ||
class Dimensions { | ||
constructor({ | ||
wrapper, | ||
content, | ||
autoResize = true, | ||
debounce: debounceValue = 250, | ||
} = {}) { | ||
this.wrapper = wrapper; | ||
this.content = content; | ||
if (autoResize) { | ||
this.debouncedResize = debounce(this.resize, debounceValue); | ||
if (this.wrapper === window) { | ||
window.addEventListener('resize', this.debouncedResize, false); | ||
} else { | ||
this.wrapperResizeObserver = new ResizeObserver(this.debouncedResize); | ||
this.wrapperResizeObserver.observe(this.wrapper); | ||
} | ||
this.contentResizeObserver = new ResizeObserver(this.debouncedResize); | ||
this.contentResizeObserver.observe(this.content); | ||
constructor({ wrapper, content, autoResize = true, debounce: debounceValue = 250, } = {}) { | ||
this.width = 0; | ||
this.height = 0; | ||
this.scrollWidth = 0; | ||
this.scrollHeight = 0; | ||
this.resize = () => { | ||
this.onWrapperResize(); | ||
this.onContentResize(); | ||
}; | ||
this.onWrapperResize = () => { | ||
if (this.wrapper === window) { | ||
this.width = window.innerWidth; | ||
this.height = window.innerHeight; | ||
} | ||
else if (this.wrapper instanceof HTMLElement) { | ||
this.width = this.wrapper.clientWidth; | ||
this.height = this.wrapper.clientHeight; | ||
} | ||
}; | ||
this.onContentResize = () => { | ||
if (this.wrapper === window) { | ||
this.scrollHeight = this.content.scrollHeight; | ||
this.scrollWidth = this.content.scrollWidth; | ||
} | ||
else if (this.wrapper instanceof HTMLElement) { | ||
this.scrollHeight = this.wrapper.scrollHeight; | ||
this.scrollWidth = this.wrapper.scrollWidth; | ||
} | ||
}; | ||
this.wrapper = wrapper; | ||
this.content = content; | ||
if (autoResize) { | ||
this.debouncedResize = debounce(this.resize, debounceValue); | ||
if (this.wrapper === window) { | ||
window.addEventListener('resize', this.debouncedResize, false); | ||
} | ||
else { | ||
this.wrapperResizeObserver = new ResizeObserver(this.debouncedResize); | ||
this.wrapperResizeObserver.observe(this.wrapper); | ||
} | ||
this.contentResizeObserver = new ResizeObserver(this.debouncedResize); | ||
this.contentResizeObserver.observe(this.content); | ||
} | ||
this.resize(); | ||
} | ||
this.resize(); | ||
} | ||
destroy() { | ||
this.wrapperResizeObserver?.disconnect(); | ||
this.contentResizeObserver?.disconnect(); | ||
window.removeEventListener('resize', this.debouncedResize, false); | ||
} | ||
resize = () => { | ||
this.onWrapperResize(); | ||
this.onContentResize(); | ||
} | ||
onWrapperResize = () => { | ||
if (this.wrapper === window) { | ||
this.width = window.innerWidth; | ||
this.height = window.innerHeight; | ||
} else { | ||
this.width = this.wrapper.clientWidth; | ||
this.height = this.wrapper.clientHeight; | ||
destroy() { | ||
var _a, _b; | ||
(_a = this.wrapperResizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect(); | ||
(_b = this.contentResizeObserver) === null || _b === void 0 ? void 0 : _b.disconnect(); | ||
window.removeEventListener('resize', this.debouncedResize, false); | ||
} | ||
} | ||
onContentResize = () => { | ||
if (this.wrapper === window) { | ||
this.scrollHeight = this.content.scrollHeight; | ||
this.scrollWidth = this.content.scrollWidth; | ||
} else { | ||
this.scrollHeight = this.wrapper.scrollHeight; | ||
this.scrollWidth = this.wrapper.scrollWidth; | ||
get limit() { | ||
return { | ||
x: this.scrollWidth - this.width, | ||
y: this.scrollHeight - this.height, | ||
}; | ||
} | ||
} | ||
get limit() { | ||
return { | ||
x: this.scrollWidth - this.width, | ||
y: this.scrollHeight - this.height, | ||
} | ||
} | ||
} | ||
class Emitter { | ||
constructor() { | ||
this.events = {}; | ||
} | ||
emit(event, ...args) { | ||
let callbacks = this.events[event] || []; | ||
for (let i = 0, length = callbacks.length; i < length; i++) { | ||
callbacks[i](...args); | ||
constructor() { | ||
this.events = {}; | ||
} | ||
} | ||
on(event, cb) { | ||
// Add the callback to the event's callback list, or create a new list with the callback | ||
this.events[event]?.push(cb) || (this.events[event] = [cb]); | ||
// Return an unsubscribe function | ||
return () => { | ||
this.events[event] = this.events[event]?.filter((i) => cb !== i); | ||
emit(event, ...args) { | ||
let callbacks = this.events[event] || []; | ||
for (let i = 0, length = callbacks.length; i < length; i++) { | ||
callbacks[i](...args); | ||
} | ||
} | ||
} | ||
off(event, callback) { | ||
this.events[event] = this.events[event]?.filter((i) => callback !== i); | ||
} | ||
destroy() { | ||
this.events = {}; | ||
} | ||
on(event, callback) { | ||
var _a; | ||
((_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.push(callback)) || (this.events[event] = [callback]); | ||
return () => { | ||
var _a; | ||
this.events[event] = (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.filter((i) => callback !== i); | ||
}; | ||
} | ||
off(event, callback) { | ||
var _a; | ||
this.events[event] = (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.filter((i) => callback !== i); | ||
} | ||
destroy() { | ||
this.events = {}; | ||
} | ||
} | ||
const LINE_HEIGHT = 100 / 6; | ||
class VirtualScroll { | ||
constructor(element, { wheelMultiplier = 1, touchMultiplier = 1 }) { | ||
this.element = element; | ||
this.wheelMultiplier = wheelMultiplier; | ||
this.touchMultiplier = touchMultiplier; | ||
this.touchStart = { | ||
x: null, | ||
y: null, | ||
}; | ||
this.emitter = new Emitter(); | ||
window.addEventListener('resize', this.onWindowResize, false); | ||
this.onWindowResize(); | ||
this.element.addEventListener('wheel', this.onWheel, { passive: false }); | ||
this.element.addEventListener('touchstart', this.onTouchStart, { | ||
passive: false, | ||
}); | ||
this.element.addEventListener('touchmove', this.onTouchMove, { | ||
passive: false, | ||
}); | ||
this.element.addEventListener('touchend', this.onTouchEnd, { | ||
passive: false, | ||
}); | ||
} | ||
// Add an event listener for the given event and callback | ||
on(event, callback) { | ||
return this.emitter.on(event, callback) | ||
} | ||
// Remove all event listeners and clean up | ||
destroy() { | ||
this.emitter.destroy(); | ||
window.removeEventListener('resize', this.onWindowResize, false); | ||
this.element.removeEventListener('wheel', this.onWheel, { | ||
passive: false, | ||
}); | ||
this.element.removeEventListener('touchstart', this.onTouchStart, { | ||
passive: false, | ||
}); | ||
this.element.removeEventListener('touchmove', this.onTouchMove, { | ||
passive: false, | ||
}); | ||
this.element.removeEventListener('touchend', this.onTouchEnd, { | ||
passive: false, | ||
}); | ||
} | ||
// Event handler for 'touchstart' event | ||
onTouchStart = (event) => { | ||
const { clientX, clientY } = event.targetTouches | ||
? event.targetTouches[0] | ||
: event; | ||
this.touchStart.x = clientX; | ||
this.touchStart.y = clientY; | ||
this.lastDelta = { | ||
x: 0, | ||
y: 0, | ||
}; | ||
this.emitter.emit('scroll', { | ||
deltaX: 0, | ||
deltaY: 0, | ||
event, | ||
}); | ||
} | ||
// Event handler for 'touchmove' event | ||
onTouchMove = (event) => { | ||
const { clientX, clientY } = event.targetTouches | ||
? event.targetTouches[0] | ||
: event; | ||
const deltaX = -(clientX - this.touchStart.x) * this.touchMultiplier; | ||
const deltaY = -(clientY - this.touchStart.y) * this.touchMultiplier; | ||
this.touchStart.x = clientX; | ||
this.touchStart.y = clientY; | ||
this.lastDelta = { | ||
x: deltaX, | ||
y: deltaY, | ||
}; | ||
this.emitter.emit('scroll', { | ||
deltaX, | ||
deltaY, | ||
event, | ||
}); | ||
} | ||
onTouchEnd = (event) => { | ||
this.emitter.emit('scroll', { | ||
deltaX: this.lastDelta.x, | ||
deltaY: this.lastDelta.y, | ||
event, | ||
}); | ||
} | ||
// Event handler for 'wheel' event | ||
onWheel = (event) => { | ||
let { deltaX, deltaY, deltaMode } = event; | ||
const multiplierX = | ||
deltaMode === 1 ? LINE_HEIGHT : deltaMode === 2 ? this.windowWidth : 1; | ||
const multiplierY = | ||
deltaMode === 1 ? LINE_HEIGHT : deltaMode === 2 ? this.windowHeight : 1; | ||
deltaX *= multiplierX; | ||
deltaY *= multiplierY; | ||
deltaX *= this.wheelMultiplier; | ||
deltaY *= this.wheelMultiplier; | ||
this.emitter.emit('scroll', { deltaX, deltaY, event }); | ||
} | ||
onWindowResize = () => { | ||
this.windowWidth = window.innerWidth; | ||
this.windowHeight = window.innerHeight; | ||
} | ||
constructor(element, { wheelMultiplier = 1, touchMultiplier = 1 }) { | ||
this.lastDelta = { | ||
x: 0, | ||
y: 0, | ||
}; | ||
this.windowWidth = 0; | ||
this.windowHeight = 0; | ||
this.onTouchStart = (event) => { | ||
const { clientX, clientY } = event.targetTouches | ||
? event.targetTouches[0] | ||
: event; | ||
this.touchStart.x = clientX; | ||
this.touchStart.y = clientY; | ||
this.lastDelta = { | ||
x: 0, | ||
y: 0, | ||
}; | ||
this.emitter.emit('scroll', { | ||
deltaX: 0, | ||
deltaY: 0, | ||
event, | ||
}); | ||
}; | ||
this.onTouchMove = (event) => { | ||
var _a, _b, _c, _d; | ||
const { clientX, clientY } = event.targetTouches | ||
? event.targetTouches[0] | ||
: event; | ||
const deltaX = -(clientX - ((_b = (_a = this.touchStart) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0)) * this.touchMultiplier; | ||
const deltaY = -(clientY - ((_d = (_c = this.touchStart) === null || _c === void 0 ? void 0 : _c.y) !== null && _d !== void 0 ? _d : 0)) * this.touchMultiplier; | ||
this.touchStart.x = clientX; | ||
this.touchStart.y = clientY; | ||
this.lastDelta = { | ||
x: deltaX, | ||
y: deltaY, | ||
}; | ||
this.emitter.emit('scroll', { | ||
deltaX, | ||
deltaY, | ||
event, | ||
}); | ||
}; | ||
this.onTouchEnd = (event) => { | ||
this.emitter.emit('scroll', { | ||
deltaX: this.lastDelta.x, | ||
deltaY: this.lastDelta.y, | ||
event, | ||
}); | ||
}; | ||
this.onWheel = (event) => { | ||
let { deltaX, deltaY, deltaMode } = event; | ||
const multiplierX = deltaMode === 1 ? LINE_HEIGHT : deltaMode === 2 ? this.windowWidth : 1; | ||
const multiplierY = deltaMode === 1 ? LINE_HEIGHT : deltaMode === 2 ? this.windowHeight : 1; | ||
deltaX *= multiplierX; | ||
deltaY *= multiplierY; | ||
deltaX *= this.wheelMultiplier; | ||
deltaY *= this.wheelMultiplier; | ||
this.emitter.emit('scroll', { deltaX, deltaY, event }); | ||
}; | ||
this.onWindowResize = () => { | ||
this.windowWidth = window.innerWidth; | ||
this.windowHeight = window.innerHeight; | ||
}; | ||
this.element = element; | ||
this.wheelMultiplier = wheelMultiplier; | ||
this.touchMultiplier = touchMultiplier; | ||
this.touchStart = { | ||
x: null, | ||
y: null, | ||
}; | ||
this.emitter = new Emitter(); | ||
window.addEventListener('resize', this.onWindowResize, false); | ||
this.onWindowResize(); | ||
this.element.addEventListener('wheel', this.onWheel, { | ||
passive: false, | ||
}); | ||
this.element.addEventListener('touchstart', this.onTouchStart, { | ||
passive: false, | ||
}); | ||
this.element.addEventListener('touchmove', this.onTouchMove, { | ||
passive: false, | ||
}); | ||
this.element.addEventListener('touchend', this.onTouchEnd, { | ||
passive: false, | ||
}); | ||
} | ||
on(event, callback) { | ||
return this.emitter.on(event, callback); | ||
} | ||
destroy() { | ||
this.emitter.destroy(); | ||
window.removeEventListener('resize', this.onWindowResize, false); | ||
this.element.removeEventListener('wheel', this.onWheel); | ||
this.element.removeEventListener('touchstart', this.onTouchStart); | ||
this.element.removeEventListener('touchmove', this.onTouchMove); | ||
this.element.removeEventListener('touchend', this.onTouchEnd); | ||
} | ||
} | ||
class Lenis { | ||
constructor({ wrapper = window, content = document.documentElement, wheelEventsTarget = wrapper, eventsTarget = wheelEventsTarget, smoothWheel = true, syncTouch = false, syncTouchLerp = 0.075, touchInertiaMultiplier = 35, duration, easing = (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), lerp = 0.1, infinite = false, orientation = 'vertical', gestureOrientation = 'vertical', touchMultiplier = 1, wheelMultiplier = 1, autoResize = true, prevent = false, __experimental__naiveDimensions = false, } = {}) { | ||
constructor({ wrapper = window, content = document.documentElement, wheelEventsTarget = wrapper, eventsTarget = wheelEventsTarget, smoothWheel = true, syncTouch = false, syncTouchLerp = 0.075, touchInertiaMultiplier = 35, duration, easing = (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), lerp = 0.1, infinite = false, orientation = 'vertical', gestureOrientation = 'vertical', touchMultiplier = 1, wheelMultiplier = 1, autoResize = true, prevent, virtualScroll, __experimental__naiveDimensions = false, } = {}) { | ||
this.__isScrolling = false; | ||
this.__isStopped = false; | ||
this.__isLocked = false; | ||
this.userData = {}; | ||
this.lastVelocity = 0; | ||
this.velocity = 0; | ||
this.direction = 0; | ||
@@ -340,3 +300,8 @@ this.onPointerDown = (event) => { | ||
}; | ||
this.onVirtualScroll = ({ deltaX, deltaY, event, }) => { | ||
this.onVirtualScroll = (data) => { | ||
if (typeof this.options.virtualScroll === 'function' && | ||
this.options.virtualScroll(data) === false) | ||
return; | ||
const { deltaX, deltaY, event } = data; | ||
this.emitter.emit('virtual-scroll', { deltaX, deltaY, event }); | ||
if (event.ctrlKey) | ||
@@ -368,3 +333,3 @@ return; | ||
return node instanceof Element && | ||
((typeof prevent === 'function' ? prevent === null || prevent === void 0 ? void 0 : prevent(node) : prevent) || | ||
((typeof prevent === 'function' && (prevent === null || prevent === void 0 ? void 0 : prevent(node))) || | ||
((_a = node.hasAttribute) === null || _a === void 0 ? void 0 : _a.call(node, 'data-lenis-prevent')) || | ||
@@ -462,2 +427,3 @@ (isTouch && ((_b = node.hasAttribute) === null || _b === void 0 ? void 0 : _b.call(node, 'data-lenis-prevent-touch'))) || | ||
prevent, | ||
virtualScroll, | ||
__experimental__naiveDimensions, | ||
@@ -464,0 +430,0 @@ }; |
@@ -1,2 +0,2 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Lenis=e()}(this,(function(){"use strict";function clamp(t,e,i){return Math.max(t,Math.min(e,i))}class Animate{advance(t){if(!this.isRunning)return;let e=!1;if(this.duration&&this.easing){this.currentTime+=t;const i=clamp(0,this.currentTime/this.duration,1);e=i>=1;const s=e?1:this.easing(i);this.value=this.from+(this.to-this.from)*s}else this.lerp?(this.value=function damp(t,e,i,s){return function lerp(t,e,i){return(1-i)*t+i*e}(t,e,1-Math.exp(-i*s))}(this.value,this.to,60*this.lerp,t),Math.round(this.value)===this.to&&(this.value=this.to,e=!0)):(this.value=this.to,e=!0);e&&this.stop(),this.onUpdate?.(this.value,e)}stop(){this.isRunning=!1}fromTo(t,e,{lerp:i,duration:s,easing:o,onStart:n,onUpdate:r}){this.from=this.value=t,this.to=e,this.lerp=i,this.duration=s,this.easing=o,this.currentTime=0,this.isRunning=!0,n?.(),this.onUpdate=r}}class Dimensions{constructor({wrapper:t,content:e,autoResize:i=!0,debounce:s=250}={}){this.wrapper=t,this.content=e,i&&(this.debouncedResize=function debounce(t,e){let i;return function(){let s=arguments,o=this;clearTimeout(i),i=setTimeout((function(){t.apply(o,s)}),e)}}(this.resize,s),this.wrapper===window?window.addEventListener("resize",this.debouncedResize,!1):(this.wrapperResizeObserver=new ResizeObserver(this.debouncedResize),this.wrapperResizeObserver.observe(this.wrapper)),this.contentResizeObserver=new ResizeObserver(this.debouncedResize),this.contentResizeObserver.observe(this.content)),this.resize()}destroy(){this.wrapperResizeObserver?.disconnect(),this.contentResizeObserver?.disconnect(),window.removeEventListener("resize",this.debouncedResize,!1)}resize=()=>{this.onWrapperResize(),this.onContentResize()};onWrapperResize=()=>{this.wrapper===window?(this.width=window.innerWidth,this.height=window.innerHeight):(this.width=this.wrapper.clientWidth,this.height=this.wrapper.clientHeight)};onContentResize=()=>{this.wrapper===window?(this.scrollHeight=this.content.scrollHeight,this.scrollWidth=this.content.scrollWidth):(this.scrollHeight=this.wrapper.scrollHeight,this.scrollWidth=this.wrapper.scrollWidth)};get limit(){return{x:this.scrollWidth-this.width,y:this.scrollHeight-this.height}}}class Emitter{constructor(){this.events={}}emit(t,...e){let i=this.events[t]||[];for(let t=0,s=i.length;t<s;t++)i[t](...e)}on(t,e){return this.events[t]?.push(e)||(this.events[t]=[e]),()=>{this.events[t]=this.events[t]?.filter((t=>e!==t))}}off(t,e){this.events[t]=this.events[t]?.filter((t=>e!==t))}destroy(){this.events={}}}const t=100/6;class VirtualScroll{constructor(t,{wheelMultiplier:e=1,touchMultiplier:i=1}){this.element=t,this.wheelMultiplier=e,this.touchMultiplier=i,this.touchStart={x:null,y:null},this.emitter=new Emitter,window.addEventListener("resize",this.onWindowResize,!1),this.onWindowResize(),this.element.addEventListener("wheel",this.onWheel,{passive:!1}),this.element.addEventListener("touchstart",this.onTouchStart,{passive:!1}),this.element.addEventListener("touchmove",this.onTouchMove,{passive:!1}),this.element.addEventListener("touchend",this.onTouchEnd,{passive:!1})}on(t,e){return this.emitter.on(t,e)}destroy(){this.emitter.destroy(),window.removeEventListener("resize",this.onWindowResize,!1),this.element.removeEventListener("wheel",this.onWheel,{passive:!1}),this.element.removeEventListener("touchstart",this.onTouchStart,{passive:!1}),this.element.removeEventListener("touchmove",this.onTouchMove,{passive:!1}),this.element.removeEventListener("touchend",this.onTouchEnd,{passive:!1})}onTouchStart=t=>{const{clientX:e,clientY:i}=t.targetTouches?t.targetTouches[0]:t;this.touchStart.x=e,this.touchStart.y=i,this.lastDelta={x:0,y:0},this.emitter.emit("scroll",{deltaX:0,deltaY:0,event:t})};onTouchMove=t=>{const{clientX:e,clientY:i}=t.targetTouches?t.targetTouches[0]:t,s=-(e-this.touchStart.x)*this.touchMultiplier,o=-(i-this.touchStart.y)*this.touchMultiplier;this.touchStart.x=e,this.touchStart.y=i,this.lastDelta={x:s,y:o},this.emitter.emit("scroll",{deltaX:s,deltaY:o,event:t})};onTouchEnd=t=>{this.emitter.emit("scroll",{deltaX:this.lastDelta.x,deltaY:this.lastDelta.y,event:t})};onWheel=e=>{let{deltaX:i,deltaY:s,deltaMode:o}=e;i*=1===o?t:2===o?this.windowWidth:1,s*=1===o?t:2===o?this.windowHeight:1,i*=this.wheelMultiplier,s*=this.wheelMultiplier,this.emitter.emit("scroll",{deltaX:i,deltaY:s,event:e})};onWindowResize=()=>{this.windowWidth=window.innerWidth,this.windowHeight=window.innerHeight}}return class Lenis{constructor({wrapper:t=window,content:e=document.documentElement,wheelEventsTarget:i=t,eventsTarget:s=i,smoothWheel:o=!0,syncTouch:n=!1,syncTouchLerp:r=.075,touchInertiaMultiplier:l=35,duration:h,easing:a=(t=>Math.min(1,1.001-Math.pow(2,-10*t))),lerp:c=.1,infinite:d=!1,orientation:p="vertical",gestureOrientation:u="vertical",touchMultiplier:m=1,wheelMultiplier:v=1,autoResize:g=!0,prevent:w=!1,__experimental__naiveDimensions:S=!1}={}){this.__isScrolling=!1,this.__isStopped=!1,this.__isLocked=!1,this.direction=0,this.onPointerDown=t=>{1===t.button&&this.reset()},this.onVirtualScroll=({deltaX:t,deltaY:e,event:i})=>{if(i.ctrlKey)return;const s=i.type.includes("touch"),o=i.type.includes("wheel");this.isTouching="touchstart"===i.type||"touchmove"===i.type;if(this.options.syncTouch&&s&&"touchstart"===i.type&&!this.isStopped&&!this.isLocked)return void this.reset();const n=0===t&&0===e,r="vertical"===this.options.gestureOrientation&&0===e||"horizontal"===this.options.gestureOrientation&&0===t;if(n||r)return;let l=i.composedPath();l=l.slice(0,l.indexOf(this.rootElement));const h=this.options.prevent;if(l.find((t=>{var e,i,n,r,l;return t instanceof Element&&(("function"==typeof h?null==h?void 0:h(t):h)||(null===(e=t.hasAttribute)||void 0===e?void 0:e.call(t,"data-lenis-prevent"))||s&&(null===(i=t.hasAttribute)||void 0===i?void 0:i.call(t,"data-lenis-prevent-touch"))||o&&(null===(n=t.hasAttribute)||void 0===n?void 0:n.call(t,"data-lenis-prevent-wheel"))||(null===(r=t.classList)||void 0===r?void 0:r.contains("lenis"))&&!(null===(l=t.classList)||void 0===l?void 0:l.contains("lenis-stopped")))})))return;if(this.isStopped||this.isLocked)return void i.preventDefault();if(!(this.options.syncTouch&&s||this.options.smoothWheel&&o))return this.isScrolling="native",void this.animate.stop();i.preventDefault();let a=e;"both"===this.options.gestureOrientation?a=Math.abs(e)>Math.abs(t)?e:t:"horizontal"===this.options.gestureOrientation&&(a=t);const c=s&&this.options.syncTouch,d=s&&"touchend"===i.type&&Math.abs(a)>5;d&&(a=this.velocity*this.options.touchInertiaMultiplier),this.scrollTo(this.targetScroll+a,Object.assign({programmatic:!1},c?{lerp:d?this.options.syncTouchLerp:1}:{lerp:this.options.lerp,duration:this.options.duration,easing:this.options.easing}))},this.onNativeScroll=()=>{if(clearTimeout(this.__resetVelocityTimeout),delete this.__resetVelocityTimeout,this.__preventNextNativeScrollEvent)delete this.__preventNextNativeScrollEvent;else if(!1===this.isScrolling||"native"===this.isScrolling){const t=this.animatedScroll;this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity,this.velocity=this.animatedScroll-t,this.direction=Math.sign(this.animatedScroll-t),this.isScrolling="native",this.emit(),0!==this.velocity&&(this.__resetVelocityTimeout=setTimeout((()=>{this.lastVelocity=this.velocity,this.velocity=0,this.isScrolling=!1,this.emit()}),400))}},window.lenisVersion="1.1.5",t&&t!==document.documentElement&&t!==document.body||(t=window),this.options={wrapper:t,content:e,wheelEventsTarget:i,eventsTarget:s,smoothWheel:o,syncTouch:n,syncTouchLerp:r,touchInertiaMultiplier:l,duration:h,easing:a,lerp:c,infinite:d,gestureOrientation:u,orientation:p,touchMultiplier:m,wheelMultiplier:v,autoResize:g,prevent:w,__experimental__naiveDimensions:S},this.animate=new Animate,this.emitter=new Emitter,this.dimensions=new Dimensions({wrapper:t,content:e,autoResize:g}),this.updateClassName(),this.userData={},this.time=0,this.velocity=this.lastVelocity=0,this.isLocked=!1,this.isStopped=!1,this.isScrolling=!1,this.targetScroll=this.animatedScroll=this.actualScroll,this.options.wrapper.addEventListener("scroll",this.onNativeScroll,!1),this.options.wrapper.addEventListener("pointerdown",this.onPointerDown,!1),this.virtualScroll=new VirtualScroll(s,{touchMultiplier:m,wheelMultiplier:v}),this.virtualScroll.on("scroll",this.onVirtualScroll)}destroy(){this.emitter.destroy(),this.options.wrapper.removeEventListener("scroll",this.onNativeScroll,!1),this.options.wrapper.removeEventListener("pointerdown",this.onPointerDown,!1),this.virtualScroll.destroy(),this.dimensions.destroy(),this.cleanUpClassName()}on(t,e){return this.emitter.on(t,e)}off(t,e){return this.emitter.off(t,e)}setScroll(t){this.isHorizontal?this.rootElement.scrollLeft=t:this.rootElement.scrollTop=t}resize(){this.dimensions.resize()}emit(){this.emitter.emit("scroll",this)}reset(){this.isLocked=!1,this.isScrolling=!1,this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity=0,this.animate.stop()}start(){this.isStopped&&(this.isStopped=!1,this.reset())}stop(){this.isStopped||(this.isStopped=!0,this.animate.stop(),this.reset())}raf(t){const e=t-(this.time||t);this.time=t,this.animate.advance(.001*e)}scrollTo(t,{offset:e=0,immediate:i=!1,lock:s=!1,duration:o=this.options.duration,easing:n=this.options.easing,lerp:r=this.options.lerp,onStart:l,onComplete:h,force:a=!1,programmatic:c=!0,userData:d={}}={}){if(!this.isStopped&&!this.isLocked||a){if("string"==typeof t&&["top","left","start"].includes(t))t=0;else if("string"==typeof t&&["bottom","right","end"].includes(t))t=this.limit;else{let i;if("string"==typeof t?i=document.querySelector(t):t instanceof HTMLElement&&(null==t?void 0:t.nodeType)&&(i=t),i){if(this.options.wrapper!==window){const t=this.rootElement.getBoundingClientRect();e-=this.isHorizontal?t.left:t.top}const s=i.getBoundingClientRect();t=(this.isHorizontal?s.left:s.top)+this.animatedScroll}}if("number"==typeof t&&(t+=e,t=Math.round(t),this.options.infinite?c&&(this.targetScroll=this.animatedScroll=this.scroll):t=clamp(0,t,this.limit),t!==this.targetScroll)){if(this.userData=d,i)return this.animatedScroll=this.targetScroll=t,this.setScroll(this.scroll),this.reset(),this.preventNextNativeScrollEvent(),this.emit(),null==h||h(this),void(this.userData={});c||(this.targetScroll=t),this.animate.fromTo(this.animatedScroll,t,{duration:o,easing:n,lerp:r,onStart:()=>{s&&(this.isLocked=!0),this.isScrolling="smooth",null==l||l(this)},onUpdate:(t,e)=>{this.isScrolling="smooth",this.lastVelocity=this.velocity,this.velocity=t-this.animatedScroll,this.direction=Math.sign(this.velocity),this.animatedScroll=t,this.setScroll(this.scroll),c&&(this.targetScroll=t),e||this.emit(),e&&(this.reset(),this.emit(),null==h||h(this),this.userData={},this.preventNextNativeScrollEvent())}})}}}preventNextNativeScrollEvent(){this.__preventNextNativeScrollEvent=!0,requestAnimationFrame((()=>{delete this.__preventNextNativeScrollEvent}))}get rootElement(){return this.options.wrapper===window?document.documentElement:this.options.wrapper}get limit(){return this.options.__experimental__naiveDimensions?this.isHorizontal?this.rootElement.scrollWidth-this.rootElement.clientWidth:this.rootElement.scrollHeight-this.rootElement.clientHeight:this.dimensions.limit[this.isHorizontal?"x":"y"]}get isHorizontal(){return"horizontal"===this.options.orientation}get actualScroll(){return this.isHorizontal?this.rootElement.scrollLeft:this.rootElement.scrollTop}get scroll(){return this.options.infinite?function modulo(t,e){return(t%e+e)%e}(this.animatedScroll,this.limit):this.animatedScroll}get progress(){return 0===this.limit?1:this.scroll/this.limit}get isScrolling(){return this.__isScrolling}set isScrolling(t){this.__isScrolling!==t&&(this.__isScrolling=t,this.updateClassName())}get isStopped(){return this.__isStopped}set isStopped(t){this.__isStopped!==t&&(this.__isStopped=t,this.updateClassName())}get isLocked(){return this.__isLocked}set isLocked(t){this.__isLocked!==t&&(this.__isLocked=t,this.updateClassName())}get isSmooth(){return"smooth"===this.isScrolling}get className(){let t="lenis";return this.isStopped&&(t+=" lenis-stopped"),this.isLocked&&(t+=" lenis-locked"),this.isScrolling&&(t+=" lenis-scrolling"),"smooth"===this.isScrolling&&(t+=" lenis-smooth"),t}updateClassName(){this.cleanUpClassName(),this.rootElement.className=`${this.rootElement.className} ${this.className}`.trim()}cleanUpClassName(){this.rootElement.className=this.rootElement.className.replace(/lenis(-\w+)?/g,"").trim()}}})); | ||
!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).Lenis=i()}(this,(function(){"use strict";function clamp(t,i,e){return Math.max(t,Math.min(i,e))}class Animate{constructor(){this.isRunning=!1,this.value=0,this.from=0,this.to=0,this.duration=0,this.currentTime=0}advance(t){var i;if(!this.isRunning)return;let e=!1;if(this.duration&&this.easing){this.currentTime+=t;const i=clamp(0,this.currentTime/this.duration,1);e=i>=1;const s=e?1:this.easing(i);this.value=this.from+(this.to-this.from)*s}else this.lerp?(this.value=function damp(t,i,e,s){return function lerp(t,i,e){return(1-e)*t+e*i}(t,i,1-Math.exp(-e*s))}(this.value,this.to,60*this.lerp,t),Math.round(this.value)===this.to&&(this.value=this.to,e=!0)):(this.value=this.to,e=!0);e&&this.stop(),null===(i=this.onUpdate)||void 0===i||i.call(this,this.value,e)}stop(){this.isRunning=!1}fromTo(t,i,{lerp:e,duration:s,easing:o,onStart:n,onUpdate:l}){this.from=this.value=t,this.to=i,this.lerp=e,this.duration=s,this.easing=o,this.currentTime=0,this.isRunning=!0,null==n||n(),this.onUpdate=l}}class Dimensions{constructor({wrapper:t,content:i,autoResize:e=!0,debounce:s=250}={}){this.width=0,this.height=0,this.scrollWidth=0,this.scrollHeight=0,this.resize=()=>{this.onWrapperResize(),this.onContentResize()},this.onWrapperResize=()=>{this.wrapper===window?(this.width=window.innerWidth,this.height=window.innerHeight):this.wrapper instanceof HTMLElement&&(this.width=this.wrapper.clientWidth,this.height=this.wrapper.clientHeight)},this.onContentResize=()=>{this.wrapper===window?(this.scrollHeight=this.content.scrollHeight,this.scrollWidth=this.content.scrollWidth):this.wrapper instanceof HTMLElement&&(this.scrollHeight=this.wrapper.scrollHeight,this.scrollWidth=this.wrapper.scrollWidth)},this.wrapper=t,this.content=i,e&&(this.debouncedResize=function debounce(t,i){let e;return function(){let s=arguments,o=this;clearTimeout(e),e=setTimeout((function(){t.apply(o,s)}),i)}}(this.resize,s),this.wrapper===window?window.addEventListener("resize",this.debouncedResize,!1):(this.wrapperResizeObserver=new ResizeObserver(this.debouncedResize),this.wrapperResizeObserver.observe(this.wrapper)),this.contentResizeObserver=new ResizeObserver(this.debouncedResize),this.contentResizeObserver.observe(this.content)),this.resize()}destroy(){var t,i;null===(t=this.wrapperResizeObserver)||void 0===t||t.disconnect(),null===(i=this.contentResizeObserver)||void 0===i||i.disconnect(),window.removeEventListener("resize",this.debouncedResize,!1)}get limit(){return{x:this.scrollWidth-this.width,y:this.scrollHeight-this.height}}}class Emitter{constructor(){this.events={}}emit(t,...i){let e=this.events[t]||[];for(let t=0,s=e.length;t<s;t++)e[t](...i)}on(t,i){var e;return(null===(e=this.events[t])||void 0===e?void 0:e.push(i))||(this.events[t]=[i]),()=>{var e;this.events[t]=null===(e=this.events[t])||void 0===e?void 0:e.filter((t=>i!==t))}}off(t,i){var e;this.events[t]=null===(e=this.events[t])||void 0===e?void 0:e.filter((t=>i!==t))}destroy(){this.events={}}}const t=100/6;class VirtualScroll{constructor(i,{wheelMultiplier:e=1,touchMultiplier:s=1}){this.lastDelta={x:0,y:0},this.windowWidth=0,this.windowHeight=0,this.onTouchStart=t=>{const{clientX:i,clientY:e}=t.targetTouches?t.targetTouches[0]:t;this.touchStart.x=i,this.touchStart.y=e,this.lastDelta={x:0,y:0},this.emitter.emit("scroll",{deltaX:0,deltaY:0,event:t})},this.onTouchMove=t=>{var i,e,s,o;const{clientX:n,clientY:l}=t.targetTouches?t.targetTouches[0]:t,r=-(n-(null!==(e=null===(i=this.touchStart)||void 0===i?void 0:i.x)&&void 0!==e?e:0))*this.touchMultiplier,h=-(l-(null!==(o=null===(s=this.touchStart)||void 0===s?void 0:s.y)&&void 0!==o?o:0))*this.touchMultiplier;this.touchStart.x=n,this.touchStart.y=l,this.lastDelta={x:r,y:h},this.emitter.emit("scroll",{deltaX:r,deltaY:h,event:t})},this.onTouchEnd=t=>{this.emitter.emit("scroll",{deltaX:this.lastDelta.x,deltaY:this.lastDelta.y,event:t})},this.onWheel=i=>{let{deltaX:e,deltaY:s,deltaMode:o}=i;e*=1===o?t:2===o?this.windowWidth:1,s*=1===o?t:2===o?this.windowHeight:1,e*=this.wheelMultiplier,s*=this.wheelMultiplier,this.emitter.emit("scroll",{deltaX:e,deltaY:s,event:i})},this.onWindowResize=()=>{this.windowWidth=window.innerWidth,this.windowHeight=window.innerHeight},this.element=i,this.wheelMultiplier=e,this.touchMultiplier=s,this.touchStart={x:null,y:null},this.emitter=new Emitter,window.addEventListener("resize",this.onWindowResize,!1),this.onWindowResize(),this.element.addEventListener("wheel",this.onWheel,{passive:!1}),this.element.addEventListener("touchstart",this.onTouchStart,{passive:!1}),this.element.addEventListener("touchmove",this.onTouchMove,{passive:!1}),this.element.addEventListener("touchend",this.onTouchEnd,{passive:!1})}on(t,i){return this.emitter.on(t,i)}destroy(){this.emitter.destroy(),window.removeEventListener("resize",this.onWindowResize,!1),this.element.removeEventListener("wheel",this.onWheel),this.element.removeEventListener("touchstart",this.onTouchStart),this.element.removeEventListener("touchmove",this.onTouchMove),this.element.removeEventListener("touchend",this.onTouchEnd)}}return class Lenis{constructor({wrapper:t=window,content:i=document.documentElement,wheelEventsTarget:e=t,eventsTarget:s=e,smoothWheel:o=!0,syncTouch:n=!1,syncTouchLerp:l=.075,touchInertiaMultiplier:r=35,duration:h,easing:a=(t=>Math.min(1,1.001-Math.pow(2,-10*t))),lerp:c=.1,infinite:d=!1,orientation:u="vertical",gestureOrientation:p="vertical",touchMultiplier:m=1,wheelMultiplier:v=1,autoResize:g=!0,prevent:w,virtualScroll:S,__experimental__naiveDimensions:f=!1}={}){this.__isScrolling=!1,this.__isStopped=!1,this.__isLocked=!1,this.userData={},this.lastVelocity=0,this.velocity=0,this.direction=0,this.onPointerDown=t=>{1===t.button&&this.reset()},this.onVirtualScroll=t=>{if("function"==typeof this.options.virtualScroll&&!1===this.options.virtualScroll(t))return;const{deltaX:i,deltaY:e,event:s}=t;if(this.emitter.emit("virtual-scroll",{deltaX:i,deltaY:e,event:s}),s.ctrlKey)return;const o=s.type.includes("touch"),n=s.type.includes("wheel");this.isTouching="touchstart"===s.type||"touchmove"===s.type;if(this.options.syncTouch&&o&&"touchstart"===s.type&&!this.isStopped&&!this.isLocked)return void this.reset();const l=0===i&&0===e,r="vertical"===this.options.gestureOrientation&&0===e||"horizontal"===this.options.gestureOrientation&&0===i;if(l||r)return;let h=s.composedPath();h=h.slice(0,h.indexOf(this.rootElement));const a=this.options.prevent;if(h.find((t=>{var i,e,s,l,r;return t instanceof Element&&("function"==typeof a&&(null==a?void 0:a(t))||(null===(i=t.hasAttribute)||void 0===i?void 0:i.call(t,"data-lenis-prevent"))||o&&(null===(e=t.hasAttribute)||void 0===e?void 0:e.call(t,"data-lenis-prevent-touch"))||n&&(null===(s=t.hasAttribute)||void 0===s?void 0:s.call(t,"data-lenis-prevent-wheel"))||(null===(l=t.classList)||void 0===l?void 0:l.contains("lenis"))&&!(null===(r=t.classList)||void 0===r?void 0:r.contains("lenis-stopped")))})))return;if(this.isStopped||this.isLocked)return void s.preventDefault();if(!(this.options.syncTouch&&o||this.options.smoothWheel&&n))return this.isScrolling="native",void this.animate.stop();s.preventDefault();let c=e;"both"===this.options.gestureOrientation?c=Math.abs(e)>Math.abs(i)?e:i:"horizontal"===this.options.gestureOrientation&&(c=i);const d=o&&this.options.syncTouch,u=o&&"touchend"===s.type&&Math.abs(c)>5;u&&(c=this.velocity*this.options.touchInertiaMultiplier),this.scrollTo(this.targetScroll+c,Object.assign({programmatic:!1},d?{lerp:u?this.options.syncTouchLerp:1}:{lerp:this.options.lerp,duration:this.options.duration,easing:this.options.easing}))},this.onNativeScroll=()=>{if(clearTimeout(this.__resetVelocityTimeout),delete this.__resetVelocityTimeout,this.__preventNextNativeScrollEvent)delete this.__preventNextNativeScrollEvent;else if(!1===this.isScrolling||"native"===this.isScrolling){const t=this.animatedScroll;this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity,this.velocity=this.animatedScroll-t,this.direction=Math.sign(this.animatedScroll-t),this.isScrolling="native",this.emit(),0!==this.velocity&&(this.__resetVelocityTimeout=setTimeout((()=>{this.lastVelocity=this.velocity,this.velocity=0,this.isScrolling=!1,this.emit()}),400))}},window.lenisVersion="1.1.6-dev.0",t&&t!==document.documentElement&&t!==document.body||(t=window),this.options={wrapper:t,content:i,wheelEventsTarget:e,eventsTarget:s,smoothWheel:o,syncTouch:n,syncTouchLerp:l,touchInertiaMultiplier:r,duration:h,easing:a,lerp:c,infinite:d,gestureOrientation:p,orientation:u,touchMultiplier:m,wheelMultiplier:v,autoResize:g,prevent:w,virtualScroll:S,__experimental__naiveDimensions:f},this.animate=new Animate,this.emitter=new Emitter,this.dimensions=new Dimensions({wrapper:t,content:i,autoResize:g}),this.updateClassName(),this.userData={},this.time=0,this.velocity=this.lastVelocity=0,this.isLocked=!1,this.isStopped=!1,this.isScrolling=!1,this.targetScroll=this.animatedScroll=this.actualScroll,this.options.wrapper.addEventListener("scroll",this.onNativeScroll,!1),this.options.wrapper.addEventListener("pointerdown",this.onPointerDown,!1),this.virtualScroll=new VirtualScroll(s,{touchMultiplier:m,wheelMultiplier:v}),this.virtualScroll.on("scroll",this.onVirtualScroll)}destroy(){this.emitter.destroy(),this.options.wrapper.removeEventListener("scroll",this.onNativeScroll,!1),this.options.wrapper.removeEventListener("pointerdown",this.onPointerDown,!1),this.virtualScroll.destroy(),this.dimensions.destroy(),this.cleanUpClassName()}on(t,i){return this.emitter.on(t,i)}off(t,i){return this.emitter.off(t,i)}setScroll(t){this.isHorizontal?this.rootElement.scrollLeft=t:this.rootElement.scrollTop=t}resize(){this.dimensions.resize()}emit(){this.emitter.emit("scroll",this)}reset(){this.isLocked=!1,this.isScrolling=!1,this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity=0,this.animate.stop()}start(){this.isStopped&&(this.isStopped=!1,this.reset())}stop(){this.isStopped||(this.isStopped=!0,this.animate.stop(),this.reset())}raf(t){const i=t-(this.time||t);this.time=t,this.animate.advance(.001*i)}scrollTo(t,{offset:i=0,immediate:e=!1,lock:s=!1,duration:o=this.options.duration,easing:n=this.options.easing,lerp:l=this.options.lerp,onStart:r,onComplete:h,force:a=!1,programmatic:c=!0,userData:d={}}={}){if(!this.isStopped&&!this.isLocked||a){if("string"==typeof t&&["top","left","start"].includes(t))t=0;else if("string"==typeof t&&["bottom","right","end"].includes(t))t=this.limit;else{let e;if("string"==typeof t?e=document.querySelector(t):t instanceof HTMLElement&&(null==t?void 0:t.nodeType)&&(e=t),e){if(this.options.wrapper!==window){const t=this.rootElement.getBoundingClientRect();i-=this.isHorizontal?t.left:t.top}const s=e.getBoundingClientRect();t=(this.isHorizontal?s.left:s.top)+this.animatedScroll}}if("number"==typeof t&&(t+=i,t=Math.round(t),this.options.infinite?c&&(this.targetScroll=this.animatedScroll=this.scroll):t=clamp(0,t,this.limit),t!==this.targetScroll)){if(this.userData=d,e)return this.animatedScroll=this.targetScroll=t,this.setScroll(this.scroll),this.reset(),this.preventNextNativeScrollEvent(),this.emit(),null==h||h(this),void(this.userData={});c||(this.targetScroll=t),this.animate.fromTo(this.animatedScroll,t,{duration:o,easing:n,lerp:l,onStart:()=>{s&&(this.isLocked=!0),this.isScrolling="smooth",null==r||r(this)},onUpdate:(t,i)=>{this.isScrolling="smooth",this.lastVelocity=this.velocity,this.velocity=t-this.animatedScroll,this.direction=Math.sign(this.velocity),this.animatedScroll=t,this.setScroll(this.scroll),c&&(this.targetScroll=t),i||this.emit(),i&&(this.reset(),this.emit(),null==h||h(this),this.userData={},this.preventNextNativeScrollEvent())}})}}}preventNextNativeScrollEvent(){this.__preventNextNativeScrollEvent=!0,requestAnimationFrame((()=>{delete this.__preventNextNativeScrollEvent}))}get rootElement(){return this.options.wrapper===window?document.documentElement:this.options.wrapper}get limit(){return this.options.__experimental__naiveDimensions?this.isHorizontal?this.rootElement.scrollWidth-this.rootElement.clientWidth:this.rootElement.scrollHeight-this.rootElement.clientHeight:this.dimensions.limit[this.isHorizontal?"x":"y"]}get isHorizontal(){return"horizontal"===this.options.orientation}get actualScroll(){return this.isHorizontal?this.rootElement.scrollLeft:this.rootElement.scrollTop}get scroll(){return this.options.infinite?function modulo(t,i){return(t%i+i)%i}(this.animatedScroll,this.limit):this.animatedScroll}get progress(){return 0===this.limit?1:this.scroll/this.limit}get isScrolling(){return this.__isScrolling}set isScrolling(t){this.__isScrolling!==t&&(this.__isScrolling=t,this.updateClassName())}get isStopped(){return this.__isStopped}set isStopped(t){this.__isStopped!==t&&(this.__isStopped=t,this.updateClassName())}get isLocked(){return this.__isLocked}set isLocked(t){this.__isLocked!==t&&(this.__isLocked=t,this.updateClassName())}get isSmooth(){return"smooth"===this.isScrolling}get className(){let t="lenis";return this.isStopped&&(t+=" lenis-stopped"),this.isLocked&&(t+=" lenis-locked"),this.isScrolling&&(t+=" lenis-scrolling"),"smooth"===this.isScrolling&&(t+=" lenis-smooth"),t}updateClassName(){this.cleanUpClassName(),this.rootElement.className=`${this.rootElement.className} ${this.className}`.trim()}cleanUpClassName(){this.rootElement.className=this.rootElement.className.replace(/lenis(-\w+)?/g,"").trim()}}})); | ||
//# sourceMappingURL=lenis.min.js.map |
{ | ||
"name": "lenis", | ||
"version": "1.1.5", | ||
"version": "1.1.6-dev.0", | ||
"author": "darkroom.engineering", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -125,21 +125,22 @@ [](https://github.com/darkroomengineering/lenis) | ||
| Option | Type | Default | Description | | ||
|--------------------------|-----------------------|----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||
| `wrapper` | `HTMLElement, Window` | `window` | The element that will be used as the scroll container | | ||
| `content` | `HTMLElement` | `document.documentElement` | The element that contains the content that will be scrolled, usually `wrapper`'s direct child | | ||
| `eventsTarget` | `HTMLElement, Window` | `wrapper` | The element that will listen to `wheel` and `touch` events | | ||
| `smoothWheel` | `boolean` | `true` | Smooth the scroll initiated by `wheel` events | | ||
| `lerp` | `number` | `0.1` | Linear interpolation (lerp) intensity (between 0 and 1) | | ||
| `duration` | `number` | `1.2` | The duration of scroll animation (in seconds). Useless if lerp defined | | ||
| `easing` | `function` | `(t) => Math.min(1, 1.001 - Math.pow(2, -10 * t))` | The easing function to use for the scroll animation, our default is custom but you can pick one from [Easings.net](https://easings.net/en). Useless if lerp defined | | ||
| `orientation` | `string` | `vertical` | The orientation of the scrolling. Can be `vertical` or `horizontal` | | ||
| `gestureOrientation` | `string` | `vertical` | The orientation of the gestures. Can be `vertical`, `horizontal` or `both` | | ||
| `syncTouch` | `boolean` | `false` | Mimic touch device scroll while allowing scroll sync (can be unstable on iOS<16) | | ||
| `syncTouchLerp` | `number` | `0.075` | Lerp applied during `syncTouch` inertia | | ||
| `touchInertiaMultiplier` | `number` | `35` | Manage the the strength of `syncTouch` inertia | | ||
| `wheelMultiplier` | `number` | `1` | The multiplier to use for mouse wheel events | | ||
| `touchMultiplier` | `number` | `1` | The multiplier to use for touch events | | ||
| `infinite` | `boolean` | `false` | Enable infinite scrolling! `syncTouch: true` is required on touch devices. ([See example](https://codepen.io/ClementRoche/pen/OJqBLod)) | | ||
| `autoResize` | `boolean` | `true` | Resize instance automatically based on `ResizeObserver`. If `false` you must resize manually using `.resize()` | | ||
| `prevent` | `boolean, function` | `false` | Manually prevent scroll to be smoothed based on elements traversed by events. Example: `(node) => node.classList.contains('cookie-modal')` | | ||
| Option | Type | Default | Description | | ||
|--------------------------|-----------------------|----------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||
| `wrapper` | `HTMLElement, Window` | `window` | The element that will be used as the scroll container | | ||
| `content` | `HTMLElement` | `document.documentElement` | The element that contains the content that will be scrolled, usually `wrapper`'s direct child | | ||
| `eventsTarget` | `HTMLElement, Window` | `wrapper` | The element that will listen to `wheel` and `touch` events | | ||
| `smoothWheel` | `boolean` | `true` | Smooth the scroll initiated by `wheel` events | | ||
| `lerp` | `number` | `0.1` | Linear interpolation (lerp) intensity (between 0 and 1) | | ||
| `duration` | `number` | `1.2` | The duration of scroll animation (in seconds). Useless if lerp defined | | ||
| `easing` | `function` | `(t) => Math.min(1, 1.001 - Math.pow(2, -10 * t))` | The easing function to use for the scroll animation, our default is custom but you can pick one from [Easings.net](https://easings.net/en). Useless if lerp defined | | ||
| `orientation` | `string` | `vertical` | The orientation of the scrolling. Can be `vertical` or `horizontal` | | ||
| `gestureOrientation` | `string` | `vertical` | The orientation of the gestures. Can be `vertical`, `horizontal` or `both` | | ||
| `syncTouch` | `boolean` | `false` | Mimic touch device scroll while allowing scroll sync (can be unstable on iOS<16) | | ||
| `syncTouchLerp` | `number` | `0.075` | Lerp applied during `syncTouch` inertia | | ||
| `touchInertiaMultiplier` | `number` | `35` | Manage the the strength of `syncTouch` inertia | | ||
| `wheelMultiplier` | `number` | `1` | The multiplier to use for mouse wheel events | | ||
| `touchMultiplier` | `number` | `1` | The multiplier to use for touch events | | ||
| `infinite` | `boolean` | `false` | Enable infinite scrolling! `syncTouch: true` is required on touch devices. ([See example](https://codepen.io/ClementRoche/pen/OJqBLod)) | | ||
| `autoResize` | `boolean` | `true` | Resize instance automatically based on `ResizeObserver`. If `false` you must resize manually using `.resize()` | | ||
| `prevent` | `function` | `undefined` | Manually prevent scroll to be smoothed based on elements traversed by events. If `true` is returned, it will prevent the scroll to be smoothed. Example: `(node) => node.classList.contains('cookie-modal')` | | ||
| `virtualScroll` | `function` | `undefined` | Manually modify the events before they get consumed. If `false` is returned, the scroll will not be smoothed. Examples: `(e) => { e.deltaY /= 2 }` (to slow down vertical scroll) or `({ event }) => !event.shiftKey` (to prevent scroll to be smoothed if shift key is pressed) | | ||
@@ -205,5 +206,6 @@ <br/> | ||
| Event | Callback Arguments | | ||
|----------|--------------------| | ||
| `scroll` | Lenis instance | | ||
| Event | Callback Arguments | | ||
|------------------|---------------------------| | ||
| `scroll` | Lenis instance | | ||
| `virtual-scroll` | `{deltaX, deltaY, event}` | | ||
@@ -216,10 +218,32 @@ | ||
### Nested scroll | ||
#### Using Javascript | ||
```html | ||
<div data-lenis-prevent>scroll content</div> | ||
<div id="modal">scrollable content</div> | ||
``` | ||
<div data-lenis-prevent-wheel>scroll content</div> | ||
```js | ||
const lenis = new Lenis({ | ||
prevent: (node) => node.id === 'modal', | ||
}) | ||
``` | ||
<div data-lenis-prevent-touch>scroll content</div> | ||
#### Using HTML | ||
```html | ||
<div data-lenis-prevent>scrollable content</div> | ||
``` | ||
prevent wheel events only | ||
```html | ||
<div data-lenis-prevent-wheel>scrollable content</div> | ||
``` | ||
prevent touch events only | ||
```html | ||
<div data-lenis-prevent-touch>scrollable content</div> | ||
``` | ||
[See modal example](https://codepen.io/ClementRoche/pen/PoLdjpw) | ||
@@ -226,0 +250,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
359168
315
20
1377
1