Lenis 平滑滚动库完整指南:从安装到上生产的快速上手
【免费下载链接】lenisSmooth scroll as it should be项目地址: https://gitcode.com/GitHub_Trending/le/lenis
Lenis 是一个几 KB、零依赖的轻量平滑滚动库,它包裹浏览器原生滚动来消除原生滚动的生硬顿挫,同时保住 sticky、锚点跳转和键盘可访问性。适合想给网站加平滑滚动、视差或 WebGL 滚动同步的前端开发者,10 分钟可以跑通。
为什么不用自己造虚拟滚动:Lenis 解决什么问题
传统平滑滚动两条路都有坑:CSSscroll-behavior: smooth只管程序化跳转,管不住滚轮手感;自己拦截滚轮做虚拟滚动,则会把position: sticky、锚点、浏览器滚动回弹、辅助功能全部打断。Lenis 的做法是"劫持输入、保留轨道"——滚动手感被接管,但滚动本身始终发生在原生滚动轨道上,页面结构不需要任何改造,也不强制改变 DOM 层级(不像部分同类库要求把内容包进 transform 容器)。典型场景:WebGL 场景滚动驱动、视差、横向滚动区、分屏锁定式翻页。
最快上手:十行代码接入 Lenis 平滑滚动
npm 安装(yarn/pnpm 同理),没有构建系统则直接在 HTML 里引 CDN 上的dist/lenis.min.js和dist/lenis.css:
npm i lenisimport Lenis from 'lenis' import 'lenis/dist/lenis.css' // 必须,否则停止态/iframe 行为不对 const lenis = new Lenis({ autoRaf: true, // 内置 rAF 循环,省去手写 requestAnimationFrame }) lenis.on('scroll', (e) => { // e.velocity / e.progress / e.direction,可驱动任意动画 })不开autoRaf时需要自己驱动每帧更新,这是官方文档里最常见的排错项,先记住:
const lenis = new Lenis() function raf(time) { lenis.raf(time) requestAnimationFrame(raf) } requestAnimationFrame(raf)工作原理:接管滚轮输入,逐帧插值滚动位置
核心逻辑在 核心实现 里,文件头部有作者自己写的五步注释,直接照它理解即可:
- 监听容器的
wheel/touch事件,并preventDefault阻止原生滚动(事件归一化在 VirtualScroll); - 把归一化后的 delta 累加到目标值
targetScroll; - 每帧由 Animate 类 把当前值
animatedScroll向targetScroll逼近——默认用指数衰减的damp插值(lerp),指定duration+easing时切换为时间基缓动; - 算出的新位置通过
scrollTo({ behavior: 'instant' })写回浏览器,也就是滚动始终跑在原生滚动轨道上; - 没有平滑动画在跑时,Lenis 退化为监听原生
scroll事件被动同步。
这套设计的直接收益:position: sticky、锚点定位、屏幕阅读器对滚动条的感知都不受影响;副作用是 iframe 内拿不到滚轮事件(它不转发),官方 CSS 里顺手把平滑滚动期间的 iframe 设成了pointer-events: none。
最常被调的 6 个参数及默认值
完整选项见 官方文档 的 Settings 表,先认这 6 个:
| 参数 | 作用 | 默认值 |
|---|---|---|
lerp | 滚轮输入的插值强度,越小越"飘" | 0.1 |
duration | 程序化滚动动画时长(秒),设了easing后生效 | 1.2 |
easing | 缓动函数,任意(t) => t形态的函数 | 内置自定义曲线 |
smoothWheel | 是否平滑滚轮事件(false时触摸仍可平滑) | true |
orientation | 滚动轴,vertical/horizontal | vertical |
autoRaf | 是否内置 rAF 循环,false必须手动raf(time) | false |
另外两个高频开关:anchors: true接管锚点链接走平滑跳转;allowNestedScroll: true让页面内嵌套滚动容器(侧栏、弹窗里的列表)保留原生滚动——但它在每次滚动事件时都要检查 DOM 树,有性能开销,注意下面"上生产前"那节。
主流框架组合:React、Vue 与 GSAP 的最小接线
Lenis 对每个框架都有一等适配包,实例通过 context 下发,不用手动传 prop。
React(lenis/react):
import { ReactLenis, useLenis } from 'lenis/react' function App() { const lenis = useLenis((lenis) => { // 每次滚动触发 }) return <ReactLenis root>{/* 页面内容 */}</ReactLenis> }root属性让实例挂到<html>滚动容器上,并且全局可通过useLenis访问。
Vue / Nuxt(lenis/vue):
// Nuxt: nuxt.config 里加一行 modules: ['lenis/nuxt'] // Vue: app.use(LenisVue)<template> <VueLenis root :options="{ autoRaf: true }" /> </template>GSAP ScrollTrigger 是它最主要的搭档,关键是把 Lenis 的raf交给 GSAP 的 ticker、让 ScrollTrigger 听 Lenis 的 scroll 事件,两边共享同一个时钟才不会不同步:
lenis.on('scroll', ScrollTrigger.update) gsap.ticker.add((time) => { lenis.raf(time * 1000) // GSAP 给秒,Lenis 要毫秒 }) gsap.ticker.lagSmoothing(0)配合框架时记得把autoRaf关掉,把raf挂进对应驱动(React 版本示例见 packages/react/README.md 的 GSAP 一节)。做分屏锁定翻页则用 lenis/snap 插件,CSS scroll-snap 在 Lenis 下不生效,别指望它:
import Snap from 'lenis/snap' const snap = new Snap(lenis) snap.addElement(document.querySelector('.section'), { align: 'start' })上生产前的注意事项:嵌套滚动、iframe 与帧率
- CSS 必须带:
lenis.css不止是修饰,它负责停止态的overflow: clip、data-lenis-prevent的overscroll-behavior等关键行为,漏掉会有诡异 bug; autoRaf是排错第一怀疑对象:不设autoRaf: true又不手动调lenis.raf(time),页面会完全不滚;- 嵌套滚动优先用属性而不是自动检测:给需要原生滚动的容器直接挂
data-lenis-prevent(还有-wheel/-touch/-vertical/-horizontal细分版本),比allowNestedScroll的全树检查便宜得多; - iframe 内容不会平滑滚:iframe 不向前滚动容器转发 wheel 事件,且平滑滚动期间 iframe 默认不可交互,内嵌视频/地图要有心理预期;
- 帧率天花板:Safari 封顶 60fps、低功耗模式 30fps(WebKit 已知限制),
requestAnimationFrame驱动的库都绕不开; - 移动端惯性需显式开启:
syncTouch让触摸手势也走平滑插值,但 iOS < 16 上行为可能不稳定,上线前在真机验证; - SPA 路由切换:跨页跳转前可开
stopInertiaOnNavigate: true或手动lenis.reset(),避免旧惯性带着新页面走。
从new Lenis({ autoRaf: true })起步,先用scroll事件驱动一个视差动画验证手感,再按项目栈接上 ScrollTrigger 或框架适配包——这条路径走完,平滑滚动就算能进生产了。
【免费下载链接】lenisSmooth scroll as it should be项目地址: https://gitcode.com/GitHub_Trending/le/lenis
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考