lenis
lenis copied to clipboard
How smooth scroll should be
Introduction
π§ Still in WIP, API might change with new releases π§
This is our take on smooth scroll, lightweight, hard working, smooth as butter scroll. See Demo
Features
- Performant
- Lightweight (~2Kb gzipped)
- Run scroll in main thread
- Accessibility (CMD+F page search, keyboard navigation, keep scroll position on page refresh, etc.)
- External RAF
- SSR proof
- Not opinionated
- Tree-shakeable
- Custom scroll easing/duration
Feature | Locomotive-scroll | GSAP ScrollSmoother | Lenis |
---|---|---|---|
Native scrollbar | β | β | β |
Listens to βwheelβ events | β | β | β |
Normalize scroll experience | β | β | β |
Accessibility | β | β | β |
CSS Sticky | β | β | β |
IntsersectionObserver | β | β | β |
Open source | β | β | β |
Built-in animation system | β | β | β |
Size (gzip) | 12.33KB | 26.08KB | 2.13kb |
Installing
using package manager:
$ npm i @studio-freight/lenis
using scripts:
<script src="https://cdn.jsdelivr.net/gh/studio-freight/[email protected]/bundled/lenis.js"></script>
Setup
Basic setup
import Lenis from '@studio-freight/lenis'
const lenis = new Lenis({
duration: 1.2,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), // https://www.desmos.com/calculator/brs54l4xou
direction: 'vertical', // vertical, horizontal
gestureDirection: 'vertical', // vertical, horizontal, both
smooth: true,
mouseMultiplier: 1,
smoothTouch: false,
touchMultiplier: 2,
infinite: false,
})
//get scroll value
lenis.on('scroll', ({ scroll, limit, velocity, direction, progress }) => {
console.log({ scroll, limit, velocity, direction, progress })
})
function raf(time) {
lenis.raf(time)
requestAnimationFrame(raf)
}
requestAnimationFrame(raf)
Using custom scroll container
const lenis = new Lenis({
wrapper: NodeElement, // element which has overflow
content: NodeElement, // usually wrapper's direct child
})
Instance settings
Option | Type | Default | Description |
---|---|---|---|
wrapper |
NodeElement |
window |
Default element which has overflow |
content |
NodeElement |
document.body |
wrapper 's direct child |
duration |
number |
1.2 |
Specifies the duration of the animation |
easing |
function |
(t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)) |
Specifies the rate of change of a specific value, our default is custom but you can pick one from Easings.net |
direction |
string |
vertical |
vertical or horizontal scrolling. |
gestureDirection |
string |
vertical |
vertical , horizontal or both . |
smooth |
boolean |
true |
Enable or disable 'smoothness' |
mouseMultiplier |
number |
1 |
This value is passed directly to Virtual Scroll |
smoothTouch |
boolean |
false |
Enable or disable 'smoothness' while scrolling using touch. Note: We have disabled it by default because touch devices native smoothness is impossible to mimic |
touchMultiplier |
number |
string |
This value is passed directly to Virtual Scroll |
infinite |
boolean |
false |
Enable infinite scrolling! |
Instance Methods
Method | Description | Arguments |
---|---|---|
raf(time) |
Must be called every frame for internal usage. | |
scrollTo(target,{offset, duration, easing, immediate}) |
Scroll to a target. | target : can be Number , NodeElement or String (CSS selector).offset : Number equivalent to scroll-padding-top.duration : Number scroll duration in seconds.easing : Function .immediate : ignore duration and easing. |
on(id,callback({scroll,limit,velocity,direction})) |
id can be any of the following instance events to listen. |
|
stop() |
To pause the scroll | |
start() |
To resume the scroll | |
destroy() |
To destroy the instance and remove all events. |
Instance Events
Event | Callback Arguments |
---|---|
scroll |
scroll : returns scroll position. limit : returns scroll limit. velocity : returns scroll velocity. direction : returns 1 or -1 . |
Considerations
Things to consider if you want to add Lenis to your codebase will be listed here.
Make sure scroll-behavior
is set to initial or not set at all (thanks @thagxt)
html {
scroll-behavior: initial;
}
Keep html and body elements default sized (see this issue)
html,
body {
min-height: 100%;
height: auto;
}
Use data-lenis-prevent
attribute on nested scroll elements. In addition, we advise you to add overscroll-behavior: contain
on this element.
<div data-lenis-prevent>scroll content</div>
Manually use lenis.scrollTo('#anchor')
on anchor link click (see this issue)
<a href="#anchor" onclick="lenis.scrollTo('#anchor')">scroll to anchor</a>
Limitations
- no support of CSS scroll-snap
- can only run 60fps maximum on Safari (source)
Tutorials
- Scroll Animation Ideas for Image Grids by Codrops
- How to Animate SVG Shapes on Scroll by Codrops
- The BEST smooth scrolling library for your Webflow website! (Lenis) by Diego Toda de Oliveira
- Easy smooth scroll in @Webflow with Lenis + GSAP ScrollTrigger tutorial by TambiΓ©n Studio
Plugins
Lenis in use
- Wyre by Studio Freight
- Lunchbox by Studio Freight
- Easol by Studio Freight
- Repeat by Studio Freight
- Dragonfly by Studio Freight
- Yuga Labs by Antinomy Studio
- Quentin Hocde's Portfolio by Quentin Hocde
- Houses Of by FΓ©lix P. & Shelby Kay
- Shelby Kay's Portfolio by Shelby Kay
- Heights Agency Portfolio by Francesco Michelini
- Goodship by Studio Freight
- Flayks' Portfolio by FΓ©lix P. & Shelby Kay
- Matt Rothenberg's portfolio by Matt Rothenberg
- Edoardo Lunardi's portfolio by Edoardo Lunardi
Authors
This set of hooks is curated and maintained by the Studio Freight Darkroom team:
- Clement Roche (@clementroche_) β Studio Freight
- Guido Fier (@uido15) β Studio Freight
- Leandro Soengas (@lsoengas) - Studio Freight
- Franco Arza (@arzafran) - Studio Freight