【免费下载链接】locomotive-scroll
🛤 Detection of elements in viewport & smooth scrolling with parallax.
本文以开源仓库 locomotive-scroll 的官方 README 为主线,系统讲解这一轻量级现代滚动库的安装、初始化、核心特性与源码实现原理。读者将掌握如何通过data-scroll系列声明式属性实现元素视口检测、视差动画与平滑滚动,并能基于源码理解双 Intersection Observer 架构、触屏降级等底层机制,直接上手落地到自己的前端项目中。
什么是 Locomotive Scroll
Locomotive Scroll 是一个轻量、现代的滚动库,核心能力是检测视口内的元素并为其提供平滑滚动与视差动画(项目源码注释原语:"Detection of elements in viewport & smooth scrolling with parallax",见 packages/lib/index.ts)。它的技术底座是 Lenis 中描述为"Detection of elements in viewport & smooth scrolling with parallax effects")。
它在设计上有几个关键取向:
- 声明式驱动:无需编写大量 JS,通过 HTML 属性(如
data-scroll-speed、data-scroll-call)即可为任意元素开启视口检测与视差效果; - TypeScript 优先:整个库完全使用 TypeScript 编写并导出完整类型定义,支持 IDE 智能提示;
- 性能敏感:通过双 Intersection Observer 区分「触发类」与「动画类」元素,避免每帧全量计算。
快速开始(Quick Start)
1. 安装
npm install locomotive-scroll从仓库的 packages/lib/package.json 可以看到,该库声明了lenis: 1.3.17作为唯一运行时依赖,并要求node >= 20。
2. 引入 JavaScript
import LocomotiveScroll from 'locomotive-scroll'; const scroll = new LocomotiveScroll();实例化时会自动完成三件事(见 packages/lib/index.ts 的_init方法):
- 创建一个 Lenis 实例(
new Lenis({ ...this.lenisOptions })),承载平滑滚动; - 在
requestAnimationFrame中创建Core实例,扫描[data-scroll]元素并注册两套 Intersection Observer; - 默认自动启动渲染循环(
autoStart默认为true)。
3. 引入基础样式
@import 'locomotive-scroll/dist/locomotive-scroll.css';4. 在 HTML 中声明滚动元素
<div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/locomotive-scroll/bundled/locomotive-scroll.css" /> <script src="https://cdn.jsdelivr.net/npm/locomotive-scroll/bundled/locomotive-scroll.min.js"></script> <script> const locomotiveScroll = new LocomotiveScroll(); </script>更完整的安装与使用细节见 安装指南 与 使用指南。
核心特性解析
README 列出了六项核心特性,下面逐条结合源码展开。
轻量(Lightweight)
- 打包体积仅9.4kB(gzipped)(README 明确标注)。作为对比,这一定位决定了它适合作为页面滚动增强层,而不是重型动画框架的替代品。
TypeScript 优先(TypeScript First)
整个库(packages/lib/index.ts、packages/lib/types.ts 及 core 目录)均为 TypeScript 实现,所有公开选项、属性、回调与实例方法都有完整类型声明。例如 packages/lib/types.ts 中定义了ILocomotiveScrollOptions:
export interface ILocomotiveScrollOptions { lenisOptions?: LenisOptions; triggerRootMargin?: string; rafRootMargin?: string; autoStart?: boolean; scrollCallback?(scrollValues: ILenisScrollValues): void; initCustomTicker?(render: () => void): void; destroyCustomTicker?(render: () => void): void; }实例化时还会把版本号写入window.locomotiveScrollVersion(见 packages/lib/index.ts),便于排查页面中运行的版本。
基于 Lenis 构建(Built on Lenis)
- 使用 Lenis最新稳定版(仓库锁定的
lenis 1.3.17),直接获得其性能优化过的平滑滚动内核。 - 所有 Lenis 实例配置通过
lenisOptions透传(见 packages/lib/index.ts),因此你既能使用 Locomotive 的声明式 API,也能完整控制 Lenis 的底层行为(lerp、duration、orientation等)。
双 Intersection Observer(Dual Intersection Observers)
这是 Locomotive Scroll 最核心的架构创新。在 packages/lib/core/Core.ts 的_init中,所有[data-scroll]元素会被拆分为两批,注册到两个独立的IntersectionObserver上:
| Observer | 默认 rootMargin | 服务对象 | 职责 |
|---|---|---|---|
| Trigger IO | '-1px -1px -1px -1px' | 仅需「进出视口」触发的元素(data-scroll、data-scroll-class、data-scroll-call等) | 触发inview/outOfView回调、添加类名,元素离开视口后若未声明data-scroll-repeat则自动unobserve(见 packages/lib/core/IO.ts) |
| RAF IO | '100% 100% 100% 100%'(上下左右各扩展一个视口尺寸) | 需要每帧计算的元素(含data-scroll-offset、data-scroll-position、data-scroll-css-progress、data-scroll-event-progress、data-scroll-speed) | 进入扩展区域后开启该元素的setInteractivityOn,纳入每帧 RAF 更新队列;离开后关闭(见 packages/lib/core/IO.ts) |
判断元素归属的_checkRafNeeded逻辑在 packages/lib/core/Core.ts:只有使用了需要连续插值计算的属性(如非默认的scrollOffset/scrollPosition、合法的scrollSpeed、scrollCssProgress/scrollEventProgress)时才走 RAF 队列,其余仅触发类元素完全由 IO 事件驱动。这避免了「所有元素每帧参与计算」的性能浪费。
智能触屏检测(Smart Touch Detection)
构造时通过'ontouchstart' in window || navigator.maxTouchPoints > 0判断是否为触屏设备(见 packages/lib/index.ts)。触屏设备上:
- 视差效果(
data-scroll-speed)默认自动禁用,以换取原生滚动的流畅度; - 渲染回调中的
smooth参数为false,ScrollElement会跳过 transform 位移(见 packages/lib/core/ScrollElement.ts); - 若确需在移动端启用视差,可给元素加
data-scroll-enable-touch-speed属性。
无障碍(Accessible)
- 保留原生滚动条(不做自定义滚动条遮罩),因此键盘、屏幕阅读器与系统滚动行为不受干扰;
- 支持键盘导航与标准 ARIA 语义,滚动定位(
scrollTo)完全基于标准 DOM 定位。
视口检测与视差的核心原理
理解data-scroll系列属性前,先看它在源码中的落地。核心类ScrollElement(packages/lib/core/ScrollElement.ts)在构造时解析元素上的全部data-*属性:
this.attributes = { scrollClass: this.$el.dataset['scrollClass'] ?? 'is-inview', scrollOffset: this.$el.dataset['scrollOffset'] ?? '0,0', scrollPosition: this.$el.dataset['scrollPosition'] ?? 'start,end', scrollCssProgress: this.$el.dataset['scrollCssProgress'] !== undefined, scrollEventProgress: this.$el.dataset['scrollEventProgress'] ?? null, scrollSpeed: this.$el.dataset['scrollSpeed'] !== undefined ? parseFloat(this.$el.dataset['scrollSpeed']) : null, scrollRepeat: this.$el.dataset['scrollRepeat'] !== undefined, scrollCall: this.$el.dataset['scrollCall'] ?? null, scrollIgnoreFold: this.$el.dataset['scrollIgnoreFold'] !== undefined, scrollEnableTouchSpeed: this.$el.dataset['scrollEnableTouchSpeed'] !== undefined, };每帧渲染时,进入 RAF 队列的元素会计算progress(0~1 的进度值),并映射为视差位移(packages/lib/core/ScrollElement.ts):
displacement = progress × containerSize × speed × -1其中containerSize是 Lenis 滚动容器的高(垂直)或宽(水平),progress对普通元素映射到-1 ~ 1、对首屏(fold)内元素映射到0 ~ 1——这就是为什么速度值相对容器尺寸而非像素,不同屏幕下效果自然缩放。数学工具函数(clamp、mapRange、normalize等)位于 packages/lib/utils/maths.ts。
data-scroll 系列属性速查
下面是仓库文档 属性参考 中定义的完整属性清单,均与 README 强调的「视口检测 + 视差」直接相关。
data-scroll(启用检测)
<div /><div><div />data-scroll-class(自定义进入类名)
- 类型:
string - 默认:
is-inview
<div><div><div>window.addEventListener('scrollEvent', (e) => { const { target, way, from } = e.detail; console.log(`target: ${target}`, `way: ${way}`, `from: ${from}`); });
data-scroll-css-progress(CSS 变量进度)
声明后,元素上会写入 CSS 变量--progress(0~1),可直接用于 CSS 动画(对应源码常量PROGRESS_CSS_VAR = '--progress',见 packages/lib/core/ScrollElement.ts):
[data-scroll-css-progress] { opacity: var(--progress); }
data-scroll-event-progress(事件进度)
元素滚动过程中持续派发指定自定义事件,detail携带target与progress(0~1):
<div>window.addEventListener('progressEvent', (e) => { const { target, progress } = e.detail; console.log(`target: ${target}`, `progress: ${progress}`); });
data-scroll-to 系列(平滑定位)
属性 说明 data-scroll-to阻止默认点击并平滑滚动到目标;目标取自元素href或data-scroll-to-href data-scroll-to-href自定义滚动目标(CSS 选择器) data-scroll-to-offset目标偏移(数字,等效scroll-padding-top) data-scroll-to-duration滚动动画时长(秒)
<a href="#section"><!-- 触屏设备默认禁用视差 --> <div>const locomotiveScroll = new LocomotiveScroll({ lenisOptions: { wrapper: window, content: document.documentElement, lerp: 0.1, duration: 1.2, orientation: 'vertical', gestureOrientation: 'vertical', smoothWheel: true, smoothTouch: false, wheelMultiplier: 1, touchMultiplier: 2, normalizeWheel: true, easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), }, });
triggerRootMargin / rafRootMargin
- 默认:
'-1px -1px -1px -1px'/'100% 100% 100% 100%'
分别控制 Trigger IO 与 RAF IO 的rootMargin(见 packages/lib/core/Core.ts 中的默认常量TRIGGER_ROOT_MARGIN与RAF_ROOT_MARGIN)。RAF 默认扩展 100% 视口,是为了让data-scroll-speed这类元素在尚未进入视口前就开始计算位移。
const locomotiveScroll = new LocomotiveScroll({ triggerRootMargin: '-1px -1px -1px -1px', rafRootMargin: '100% 100% 100% 100%', });
autoStart
- 类型:
boolean,默认:true
关闭自动启动后可手动控制渲染循环:
const locomotiveScroll = new LocomotiveScroll({ autoStart: false }); setTimeout(() => { locomotiveScroll.start(); }, 2000);
scrollCallback
- 类型:
function
订阅 Lenis 的scroll事件,回调参数为{ scroll, limit, velocity, direction, progress }(类型见 packages/lib/types.ts 的ILenisScrollValues):
function onScroll({ scroll, limit, velocity, direction, progress }) { console.log(scroll, limit, velocity, direction, progress); } const locomotiveScroll = new LocomotiveScroll({ scrollCallback: onScroll });
initCustomTicker / destroyCustomTicker
- 类型:
function
用外部 ticker(如 GSAP)替换默认的requestAnimationFrame循环。两者必须成对声明,否则会在控制台给出警告(见 packages/lib/index.ts):
import { gsap } from 'gsap/all'; const locomotiveScroll = new LocomotiveScroll({ initCustomTicker: (render) => { gsap.ticker.add(render); }, destroyCustomTicker: (render) => { gsap.ticker.remove(render); }, });
实例方法(Methods)
完整参考见 方法文档,所有方法在 packages/lib/index.ts 中均有对应实现。
方法 说明 start()/stop()手动启动/停止渲染循环(配合autoStart: false使用;start/stop实现见 packages/lib/index.ts) destroy()销毁实例:停止循环、解绑事件、销毁 Lenis 与 Core(见 packages/lib/index.ts) resize()手动触发重算;库已通过 Lenis 的onContentResize/onWrapperResize自动同步尺寸变化(见 packages/lib/index.ts),一般无需手动调用 scrollTo(target, options)平滑滚动到目标;target可为数字、HTMLElement或字符串(CSS 选择器/关键字top、bottom等);options支持offset、lerp、duration、immediate、lock、force、easing、onComplete(见 packages/lib/index.ts) addScrollElements($newContainer)/removeScrollElements($oldContainer)动态 DOM(Ajax 渲染等场景)下增量注册/注销容器内的[data-scroll]元素(见 packages/lib/index.ts 与 packages/lib/core/Core.ts)
const locomotiveScroll = new LocomotiveScroll({ autoStart: false }); requestAnimationFrame(() => { locomotiveScroll.start(); }); // 动态插入内容后: const $newContainer = document.getElementById('containerToAdd'); locomotiveScroll.addScrollElements($newContainer);
水平滚动与自定义滚动容器
水平滚动
orientation: 'horizontal'时,推荐附加以下 CSS(文档 使用指南 明确建议):
/* Only necessary with horizontal scrolling */ html[data-scroll-orientation='horizontal'] { body { width: fit-content; } [data-scroll-container] { display: flex; } }
自定义滚动容器
const locomotiveScroll = new LocomotiveScroll({ lenisOptions: { wrapper: document.querySelector('.scroll-container'), content: document.querySelector('.scroll-content'), }, });
<div class="scroll-container" style="height: 100vh; overflow: hidden;"> <div class="scroll-content"> <div>赞
【免费下载链接】locomotive-scroll
🛤 Detection of elements in viewport & smooth scrolling with parallax.
相关推荐
Locomotive Scroll v5 技术详解:基于 Lenis 的视口检测、平滑滚动与视差动画实践
Locomotive Scroll v5 技术详解:基于 Lenis 的视口检测、平滑滚动与视差动画实践 导读 本文是 Locomotive Scroll v5
Locomotive Scroll 5.x 使用指南:视口检测、视差平滑滚动与 Lenis 集成
Locomotive Scroll 5.x 使用指南:视口检测、视差平滑滚动与 Lenis 集成 本指南围绕当前仓库中 packages/lib 下的核心库展开
Locomotive-Scroll 终极指南:从零掌握视差滚动与视口检测
Locomotive Scroll 终极指南:从零掌握视差滚动与视口检测 🚂 Locomotive Scroll 是一个功能强大的JavaScript库,专门