news 2026/9/25 4:56:18

Locomotive Scroll 实战指南:基于 Lenis 的轻量级视口检测与平滑滚动视差方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Locomotive Scroll 实战指南:基于 Lenis 的轻量级视口检测与平滑滚动视差方案

【免费下载链接】locomotive-scroll

🛤 Detection of elements in viewport & smooth scrolling with parallax.

项目地址:https://gitcode.com/gh_mirrors/lo/locomotive-scroll
点击查看免费下载

本文以开源仓库 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方法):

  1. 创建一个 Lenis 实例(new Lenis({ ...this.lenisOptions })),承载平滑滚动;
  2. 在requestAnimationFrame中创建Core实例,扫描[data-scroll]元素并注册两套 Intersection Observer;
  3. 默认自动启动渲染循环(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.

项目地址:https://gitcode.com/gh_mirrors/lo/locomotive-scroll
点击查看免费下载
上一篇:ConEmu高级配置:打造个性化Windows终端环境
下一篇:3步掌握柔性车间智能调度:图神经网络与强化学习实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 4:53:29

边缘AI芯片选型实战:从场景反推算力、功耗与内存带宽

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:53:21

Delphi 13.1 跨平台开发:TMS FNC UI Pack 源码版安装与多端实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:52:16

HG680-LC刷机教程:S905L3B升级安卓9,解锁全网通去广告

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:49:44

AI出海算力优化与Agent落地:从堆卡到拼效率的工程实践

1. 从"堆卡"到"拼效率"&#xff1a;算力反超背后的真实账本2025年做AI出海&#xff0c;如果还把注意力全放在"谁家卡多"上&#xff0c;基本已经落后半个身位了。过去两年我参与过几个面向海外市场的AI产品从0到1&#xff0c;最深的感受是&#x…

作者头像 李华

关于博客

这是一个专注于编程技术分享的极简博客,旨在为开发者提供高质量的技术文章和教程。

订阅更新

输入您的邮箱,获取最新文章更新。

© 2025 极简编程博客. 保留所有权利.