Lenis
How smooth scroll should be
README
Introduction
This is our take on smooth scroll, lightweight, hard-working, smooth as butter scroll. See Demo.
Features
- Run scroll in the main thread
- Performant
- Lightweight (<4Kb gzipped)
- Keep CSS Sticky and IntersectionObserver
- Accessibility (CMD+F page search, keyboard navigation, keep scroll position on page refresh, etc.)
- External RAF
- SSR proof
- Custom scroll easing and duration
| Feature | [Locomotive-scroll](https://github.com/locomotivemtl/locomotive-scroll) | [GSAP | [Lenis](https://github.com/studio-freight/lenis) | 
|---|---|---|---|
| |-----------------------------|-------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| | |||
| Native | ❌ | ✅ | ✅ | 
| Native | ❌ | ✅ | ❌ | 
| Normalize | ✅ | ❌ | ✅ | 
| Accessibility | ❌ | ❌ | ✅ | 
| CSS | ❌ | ❌ | ✅ | 
| IntsersectionObserver | ❌ | ❌ | ✅ | 
| Open | ✅ | ❌ | ✅ | 
| Built-in | ✅ | ✅ | ❌ | 
| Size | [12.1KB](https://bundlephobia.com/package/locomotive-scroll@4.1.4) | [26.08KB](https://bundlejs.com/?q=gsap%2FScrollSmoother&treeshake=%5B%7BScrollSmoother%7D%5D) | [3.6KB](https://bundlephobia.com/package/@studio-freight/lenis@1.0.0-dev.8) | 
Installing
using a package manager:
- ```bash
- $ npm i @studio-freight/lenis
- ```
- ```js
- import Lenis from '@studio-freight/lenis'
- ```
using scripts:
- ```htmt
- <script src="https://cdn.jsdelivr.net/gh/studio-freight/lenis@1.0.0/bundled/lenis.js"></script>
- ```
Setup
Basic setup:
- ```js
- const lenis = new Lenis()
- lenis.on('scroll', (e) => {
- console.log(e)
- })
- function raf(time) {
- lenis.raf(time)
- requestAnimationFrame(raf)
- }
- requestAnimationFrame(raf)
- ```
Instance settings
| Option | Type | Default | Description | 
|---|---|---|---|
| |----------------------|----------------------|----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| | |||
| `wrapper` | `HTMLElement|Window` | `window` | The | 
| `content` | `HTMLElement` | `document.documentElement` | The | 
| `lerp` | `number` | `0.1` | Linear | 
| `duration` | `number` | `1.2` | The | 
| `easing` | `function` | `(t) | The | 
| `orientation` | `string` | `vertical` | The | 
| `gestureOrientation` | `string` | `vertical` | The | 
| `smoothWheel` | `boolean` | `true` | Whether | 
| `smoothTouch` | `boolean` | `false` | Whether | 
| `wheelMultiplier` | `number` | `1` | The | 
| `touchMultiplier` | `number` | `2` | The | 
| `normalizeWheel` | `boolean` | `true` | Normalize | 
| `infinite` | `boolean` | `false` | Enable | 
<!-- target: goal to reach
- number: value to scroll in pixels
- string: CSS selector or keyword (top, left, start, bottom, right, end)
- HTMLElement: DOM element
options:
- offset(number): equivalent to [scroll-padding-top](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-padding-top)
- lerp(number): animation lerp intensity
- duration(number): animation duration (in seconds)
- easing(function): animation easing
- immediate(boolean): ignore duration, easing and lerp
- lock(boolean): whether or not to prevent user from scrolling until target reached
- onComplete(function): called when target is reached -->
Instance Methods
| Method | Description | Arguments | 
|---|---|---|
| |-----------------------------|---------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||
| `raf(time)` | Must | `time`: | 
| `scrollTo(target, | Scroll | `target`: | 
| `on(id, | `id` | | | 
| `stop()` | Pauses | | | 
| `start()` | Resumes | | | 
| `destroy()` | Destroys | | | 
Instance Events
| Event | Callback | 
|---|---|
| |----------|--------------------| | |
| `scroll` | Lenis | 
Considerations
Make sure scroll-behavior is set to auto or not set at all
- ```css
- .lenis.lenis-smooth {
- scroll-behavior: auto;
- }
- ```
Keep HTML elements default sized, this is necessary for Webflow implementation (see issue)
- ```css
- html.lenis {
- height: auto;
- }
- ```
Use the data-lenis-prevent attribute on nested scroll elements. In addition, we advise you to add overscroll-behavior: contain on this element
- ```html
- <div data-lenis-prevent>scroll content</div>
- ```
- ```css
- .lenis.lenis-smooth [data-lenis-prevent] {
- overscroll-behavior: contain;
- }
- ```
Manually use lenis.scrollTo('#anchor') on anchor link click (see issue)
- ```html
- <a href="#anchor" onclick="lenis.scrollTo('#anchor')">scroll to anchor</a>
- ```
Hide overflow when lenis is stopped
- ```css
- .lenis.lenis-stopped {
- overflow: hidden;
- }
- ```
GSAP ScrollTrigger integration
- ```js
- lenis.on('scroll', ScrollTrigger.update)
- gsap.ticker.add((time)=>{
- lenis.raf(time * 1000)
- })
- ```
Limitations
- no support for CSS scroll-snap
- capped to 60fps on Safari (source)
- 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
- r3f-scroll-rig by 14islands
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
- Goodship by Studio Freight
- DeSo by Studio Freight
Authors
This set of hooks is curated and maintained by the Studio Freight Darkroom team:
- Clément Roche (@clementroche\_) – Studio Freight
- Guido Fier (@uido15) – Studio Freight
- Leandro Soengas (@lsoengas) - Studio Freight
- Franco Arza (@arzafran) - Studio Freight
 探客时代
探客时代

