@studio-freight/lenis
Version:
Lenis is a smooth scroll library to normalize and smooth the scrolling experience across devices
278 lines (201 loc) • 22.4 kB
Markdown
[](https://github.com/darkroomengineering/lenis)
[](https://www.npmjs.com/package/@studio-freight/lenis)
[](https://www.npmjs.com/package/@studio-freight/lenis)
[](https://bundlephobia.com/package/@studio-freight/lenis)
## Introduction
This is our take on smooth scroll, lightweight, hard-working, smooth as butter scroll. See [Demo](https://lenis.darkroom.engineering/).
<br>
## Installation
### JavaScript
using a package manager:
```bash
npm i @studio-freight/lenis
```
```js
import Lenis from '@studio-freight/lenis'
```
<br/>
using scripts:
```html
<script src="https://unpkg.com/@studio-freight/lenis@1.0.42/dist/lenis.min.js"></script>
```
<br>
## Setup
### Basic:
```js
const lenis = new Lenis()
lenis.on('scroll', (e) => {
console.log(e)
})
function raf(time) {
lenis.raf(time)
requestAnimationFrame(raf)
}
requestAnimationFrame(raf)
```
### GSAP ScrollTrigger:
```js
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](https://github.com/darkroomengineering/lenis/tree/main/packages/react-lenis).
<br/>
## 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](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` |
| `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](https://codepen.io/ClementRoche/pen/OJqBLod)) |
| `autoResize` | `boolean` | `true` | Resize instance automatically based on `ResizeObserver`. If `false` you must resize manually using `.resize()` |
<br/>
<!-- `target`: goal to reach
- `number`: value to scroll in pixels
- `string`: CSS selector or keyword (`top`, `left`, `start`, `bottom`, `right`, `end`)
- `HTMLElement`: DOM element
<br/>
`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 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 |
<br/>
## 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 reach<ul><li>`number`: value to scroll in pixels</li><li>`string`: CSS selector or keyword (`top`, `left`, `start`, `bottom`, `right`, `end`)</li><li>`HTMLElement`: DOM element</li></ul>`options`<ul><li>`offset`(`number`): equivalent to [`scroll-padding-top`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-padding-top)</li><li>`lerp`(`number`): animation lerp intensity</li><li>`duration`(`number`): animation duration (in seconds)</li><li>`easing`(`function`): animation easing</li><li>`immediate`(`boolean`): ignore duration, easing and lerp</li><li>`lock`(`boolean`): whether or not to prevent the user from scrolling until the target is reached</li><li>`force`(`boolean`): reach target even if instance is stopped</li><li>`onComplete`(`function`): called when the target is reached</li></ul> |
| `on(id, function)` | `id` can be any of the following [instance events](#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 |
<br/>
## Recommended CSS
```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;
}
```
<br/>
## Considerations
### Nested scroll
```html
<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](https://codepen.io/ClementRoche/pen/PoLdjpw)
### Anchor links
```html
<a href="#anchor" onclick="lenis.scrollTo('#anchor')">scroll to anchor</a>
```
<br>
## Limitations
- no support for CSS scroll-snap
- capped to 60fps on Safari ([source](https://bugs.webkit.org/show_bug.cgi?id=173434)) 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](https://github.com/darkroomengineering/lenis/issues/103))
<br>
## Tutorials
- [Scroll Animation Ideas for Image Grids](https://tympanus.net/Development/ScrollAnimationsGrid/) by [Codrops](https://tympanus.net/codrops)
- [How to Animate SVG Shapes on Scroll](https://tympanus.net/codrops/2022/06/08/how-to-animate-svg-shapes-on-scroll) by [Codrops](https://tympanus.net/codrops)
- [The BEST smooth scrolling library for your Webflow website! (Lenis)](https://www.youtube.com/watch?v=VtCqTLRRMII) by [Diego Toda de Oliveira](https://www.diegoliv.works/)
- [Easy smooth scroll in @Webflow with Lenis + GSAP ScrollTrigger tutorial](https://www.youtube.com/watch?v=gRKuzQTXq74) by [También Studio](https://www.tambien.studio/)
<br>
## Plugins
- [Loconative-scroll](https://github.com/quentinhocde/loconative-scroll#how-to-switch-from-locomotive-scroll-to-loconative-scroll) by [Quentin Hocde](https://twitter.com/QuentinHocde)
- [react-lenis](https://github.com/darkroomengineering/lenis/tree/main/packages/react-lenis) by [darkroom.engineering](https://darkroom.engineering/)
- [r3f-scroll-rig](https://github.com/14islands/r3f-scroll-rig) by [14islands](https://14islands.com/)
- [Lenis Scroll Snap Plugin](https://github.com/funkhaus/lenis-scroll-snap) by [Funkhaus](https://github.com/funkhaus)
- [locomotive-scroll](https://github.com/locomotivemtl/locomotive-scroll) by [Locomotive](https://locomotive.ca/)
- [vue-lenis](https://github.com/zeokku/vue-lenis) by [ZEOKKU](https://zeokku.com/)
- [nuxt-lenis](https://www.npmjs.com/package/nuxt-lenis) by [Milkshake Studio](https://milkshake.studio/)
<br>
## Lenis in use
- [Lunchbox](https://lunchbox.io/) by [Studio Freight](https://www.studiofreight.com/)
- [Easol](https://easol.com/) by [Studio Freight](https://www.studiofreight.com/)
- [Dragonfly](https://dragonfly.xyz/) by [Studio Freight](https://www.studiofreight.com/)
- [Yuga Labs](https://yuga.com/) by [Antinomy Studio](https://antinomy.studio/)
- [Quentin Hocde's Portfolio](https://quentinhocde.com) by [Quentin Hocde](https://twitter.com/QuentinHocde)
- [Houses Of](https://housesof.world) by [Félix P.](https://flayks.com/) & [Shelby Kay](https://shelbykay.dev/)
- [Shelby Kay's Portfolio](https://shelbykay.dev) by [Shelby Kay](https://shelbykay.dev/)
- [Heights Agency Portfolio](https://www.heights.agency/) by [Francesco Michelini](https://www.francescomichelini.com/)
- [Goodship](https://goodship.io) by [Studio Freight](https://www.studiofreight.com/)
- [Flayks' Portfolio](https://flayks.com) by [Félix P.](https://flayks.com/) & [Shelby Kay](https://shelbykay.dev/)
- [Matt Rothenberg's portfolio](https://mattrothenberg.com) by [Matt Rothenberg](https://twitter.com/mattrothenberg)
- [Edoardo Lunardi's portfolio](https://www.edoardolunardi.dev/) by [Edoardo Lunardi](https://www.edoardolunardi.dev/)
- [DeSo](https://deso.com) by [Studio Freight](https://www.studiofreight.com/)
- [Francesco Michelini's portfolio](https://www.francescomichelini.com/projects) by [Francesco Michelini](https://www.francescomichelini.com/)
<br/>
## Authors
This set of hooks is curated and maintained by the darkroom.engineering team:
- Clément Roche ([@clementroche\_](https://twitter.com/clementroche_)) – [darkroom.engineering](https://darkroom.engineering)
- Guido Fier ([@uido15](https://twitter.com/uido15)) – [darkroom.engineering](https://darkroom.engineering)
- Leandro Soengas ([@lsoengas](https://twitter.com/lsoengas)) - [darkroom.engineering](https://darkroom.engineering)
- Franco Arza ([@arzafran](https://twitter.com/arzafran)) - [darkroom.engineering](https://darkroom.engineering)
<br/>
## License
[The MIT License.](https://opensource.org/licenses/MIT)