Introduction
This is our take on smooth scroll, lightweight, hard-working, smooth as butter scroll. See Demo.
Installation
JavaScript
using a package manager:
npm i @studio-freight/lenis
import Lenis from '@studio-freight/lenis'
using scripts:
<script src="https://unpkg.com/@studio-freight/lenis@1.0.42/dist/lenis.min.js"></script>
Setup
Basic:
const lenis = new Lenis()
lenis.on('scroll', (e) => {
console.log(e)
})
function raf(time) {
lenis.raf(time)
requestAnimationFrame(raf)
}
requestAnimationFrame(raf)
GSAP ScrollTrigger:
const lenis = new Lenis()
lenis.on('scroll', (e) => {
console.log(e)
})
lenis.on('scroll', ScrollTrigger.update)
gsap.ticker.add((time)=>{
lenis.raf(time * 1000)
})
gsap.ticker.lagSmoothing(0)
React:
See documentation for react-lenis.
Instance settings
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 |
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. 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 |
smoothWheel | boolean | true | Whether or not to enable smooth scrolling for mouse wheel events |
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 |
normalizeWheel | boolean | false | Normalize wheel inputs across browsers (not reliable atm) |
infinite | boolean | false | Enable infinite scrolling! (See example) |
autoResize | boolean | true | Resize instance automatically based on ResizeObserver . If false you must resize manually using .resize() |
Instance Props
Property | Type | Description |
---|
animatedScroll | number | Current scroll value |
dimensions | object | Dimensions instance |
direction | number | 1 : scrolling up, -1 : scrolling down |
emitter | object | Emitter instance |
options | object | Instance options |
targetScroll | number | Target scroll value |
time | number | Time elapsed since instance creation |
actualScroll | number | Current scroll value registered by the browser |
velocity | number | Current scroll velocity |
isHorizontal (getter) | boolean | Whether or not the instance is horizontal |
isScrolling (getter) | boolean | Whether or not the scroll is being animated |
isSmooth (getter) | boolean | Whether or not the scroll is animated |
isStopped (getter) | boolean | Whether or not the user should be able to scroll |
limit (getter) | number | Maximum scroll value |
progress (getter) | number | Scroll progress from 0 to 1 |
rootElement (getter) | HTMLElement | Element on which Lenis is instanced |
scroll (getter) | number | Current scroll value (handles infinite scroll if activated) |
className (getter) | string | rootElement className |
Instance Methods
Method | Description | Arguments |
---|
raf(time) | Must be called every frame for internal usage. | time : in ms |
scrollTo(target, options) | Scroll to target. | target : goal to reachnumber : value to scroll in pixelsstring : CSS selector or keyword (top , left , start , bottom , right , end )HTMLElement : DOM element
options offset (number ): equivalent to scroll-padding-top lerp (number ): animation lerp intensityduration (number ): animation duration (in seconds)easing (function ): animation easingimmediate (boolean ): ignore duration, easing and lerplock (boolean ): whether or not to prevent the user from scrolling until the target is reachedforce (boolean ): reach target even if instance is stoppedonComplete (function ): called when the target is reached
|
on(id, function) | id can be any of the following instance events to listen. | |
stop() | Pauses the scroll | |
start() | Resumes the scroll | |
resize() | Compute internal sizes, it has to be used if autoResize option is false . | |
destroy() | Destroys the instance and removes all events. | |
Instance Events
Event | Callback Arguments |
---|
scroll | Lenis instance |
Recommended CSS
html.lenis, html.lenis body {
height: auto;
}
.lenis.lenis-smooth {
scroll-behavior: auto !important;
}
.lenis.lenis-smooth [data-lenis-prevent] {
overscroll-behavior: contain;
}
.lenis.lenis-stopped {
overflow: hidden;
}
.lenis.lenis-scrolling iframe {
pointer-events: none;
}
Considerations
Nested scroll
<div data-lenis-prevent>scroll content</div>
<div data-lenis-prevent-wheel>scroll content</div>
<div data-lenis-prevent-touch>scroll content</div>
See modal example
Anchor links
<a href="#anchor" onclick="lenis.scrollTo('#anchor')">scroll to anchor</a>
Limitations
- no support for CSS scroll-snap
- capped to 60fps on Safari (source) and 30fps on low power mode
- smooth scroll will stop working over iframe since they don't forward wheel events
- position fixed seems to lag on MacOS Safari pre-M1 (source)
Tutorials
Plugins
Lenis in use
Authors
This set of hooks is curated and maintained by the darkroom.engineering team:
License
The MIT License.