Lenis Smooth Scroll
Warum fühlt sich native Scrolling "snappy" an — und wie Lenis das fixt.
❌ Das Problem mit Browser-Scroll
Native Browser-Scrolling springt direkt zur Zielposition.
Wheel Event → scrollTop = 500px // SOFORT
Das fühlt sich "snappy" an. Kein Gewicht, keine Trägheit, keine Physik.
✅ Die Lösung: Linear Interpolation (LERP)
Lenis interpoliert zwischen aktuellem und Ziel-Wert.
Frame 1: scroll = 0, target = 500 → scroll = 50
Frame 2: scroll = 50, target = 500 → scroll = 95
Frame 3: scroll = 95, target = 500 → scroll = 135
... smooth approach ...
lerp: 0.1 = 10% des Weges pro Frame → buttrig smooth
LERP — Das Herzstück
// Die Formel
newValue = currentValue + (targetValue - currentValue) * lerp
lerp: 0.1 Sehr smooth. Langsam. Premium-Feel.
lerp: 0.3 Balanced. Smooth aber responsive.
lerp: 1.0 Instant. Wie Browser-native.
Basic Setup
1. Installation:
npm install lenis
2. Minimal Setup (mit autoRaf):
import Lenis from 'lenis'
import 'lenis/dist/lenis.css'
const lenis = new Lenis({
autoRaf: true, // Automatischer RAF-Loop
lerp: 0.1, // Smooth-Faktor
})
// Optional: auf Scroll Events hören
lenis.on('scroll', (e) => {
console.log(e.scroll, e.velocity)
}) GSAP ScrollTrigger Integration
import Lenis from 'lenis'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
const lenis = new Lenis()
// 1. Lenis informiert ScrollTrigger über Scroll-Position
lenis.on('scroll', ScrollTrigger.update)
// 2. Lenis läuft im GSAP Ticker (perfekter Sync)
gsap.ticker.add((time) => {
lenis.raf(time * 1000)
})
// 3. Kein Lag-Smoothing (würde Sync brechen)
gsap.ticker.lagSmoothing(0) Warum funktioniert das? Lenis macht das Smooth-Scrolling, ScrollTrigger bekommt die interpolierten Werte und triggert Animationen basierend darauf.
Wichtige Properties
lenis.scroll Aktueller (interpolierter) Scroll-Wert
lenis.targetScroll Ziel-Wert (wo User hin will)
lenis.velocity Scroll-Geschwindigkeit (für Effekte)
lenis.progress 0 bis 1 (für Progress-Bars)
lenis.direction 1 = down, -1 = up
lenis.isScrolling "smooth" | "native" | false
Wichtige Methoden
lenis.scrollTo(target, options) Programmatisch scrollen.
lenis.scrollTo('#section', { offset: -100, duration: 2 })
lenis.scrollTo(500, { immediate: true }) // Sofort, kein smooth lenis.stop() / lenis.start() Scrolling pausieren/fortsetzen. Für Modals!
lenis.destroy() Cleanup. Wichtig bei React/Vue unmount!
Options verstehen
new Lenis({
// Smooth-Verhalten
lerp: 0.1, // 0.05-0.2 ist sweet spot
duration: 1.2, // Ignoriert wenn lerp gesetzt!
easing: (t) => ..., // Ignoriert wenn lerp gesetzt!
// Scroll-Richtung
orientation: 'vertical', // oder 'horizontal'
gestureOrientation: 'both', // Touch-Gesten
// Performance
smoothWheel: true, // Wheel Events smoothen
syncTouch: false, // Touch smoothen (instabil auf iOS!)
// Nested Scrolling
prevent: (node) => node.classList.contains('modal'),
// Anchor Links
anchors: true, // Smooth scroll zu #anchors
}) Achtung: duration und easing werden von lerp überschrieben!
Verwende entweder LERP oder Duration/Easing, nicht beides.
Nested Scrolling (Modals, etc.)
Mit HTML-Attribut:
<div data-lenis-prevent> <!-- Normales Scrolling hier --> </div>
Mit JavaScript:
new Lenis({
prevent: (node) => {
return node.id === 'modal'
}
}) React Integration
import { ReactLenis, useLenis } from 'lenis/react'
// Provider
function App() {
return (
<ReactLenis root options={{ lerp: 0.1 }}>
<YourContent />
</ReactLenis>
)
}
// Hook
function Component() {
const lenis = useLenis()
const handleClick = () => {
lenis?.scrollTo('#target', { duration: 1.5 })
}
return <button onClick={handleClick}>Scroll</button>
} Performance-Tipps
autoRaf: true für einfache Setups syncTouch: false auf iOS (kann instabil sein) ✅ Was ich WIRKLICH verstanden habe
- • LERP ist das Kernkonzept — interpoliert zwischen current und target
- • lerp: 0.1 = 10% pro Frame → smooth, lerp: 1 = instant
- • duration/easing werden von lerp überschrieben — nicht mischen!
- • GSAP Integration: lenis.on('scroll') + gsap.ticker.add()
- • data-lenis-prevent für nested scrolling (Modals)
- • lenis.stop() / start() für Modal-Overlays