locomotive-scroll by freshtechbro/claudedesignskills
npx skills add https://github.com/freshtechbro/claudedesignskills --skill locomotive-scroll使用 Locomotive Scroll 实现平滑滚动、视差效果和滚动驱动动画的综合指南。
Locomotive Scroll 是一个 JavaScript 库,提供:
何时使用 Locomotive Scroll:
权衡考虑:
npm install locomotive-scroll
// ES6
import LocomotiveScroll from 'locomotive-scroll';
import 'locomotive-scroll/dist/locomotive-scroll.css';
// 或通过 CDN
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/locomotive-scroll/dist/locomotive-scroll.min.css">
<script src="https://cdn.jsdelivr.net/npm/locomotive-scroll/dist/locomotive-scroll.min.js"></script>
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
每个 Locomotive Scroll 实现都需要特定的数据属性:
<!-- 滚动容器(必需) -->
<div data-scroll-container>
<!-- 滚动区块(可选,提升性能) -->
<div data-scroll-section>
<!-- 被追踪的元素 -->
<h1 data-scroll>基础检测</h1>
<!-- 视差元素 -->
<div data-scroll data-scroll-speed="2">
比滚动移动得更快
</div>
<!-- 粘性元素 -->
<div data-scroll data-scroll-sticky>
在区块内保持粘性
</div>
<!-- 带有 ID 以供追踪的元素 -->
<div data-scroll data-scroll-id="hero">
可通过 JavaScript 访问
</div>
<!-- 调用事件触发器 -->
<div data-scroll data-scroll-call="fadeIn">
触发自定义事件
</div>
</div>
</div>
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true,
lerp: 0.1, // 平滑度 (0-1,值越低越平滑)
multiplier: 1, // 速度乘数
class: 'is-inview', // 添加到可见元素的类名
repeat: false, // 重复视口内检测
offset: [0, 0] // 全局触发偏移量 [底部, 顶部]
});
| 属性 | 用途 | 示例 |
|---|---|---|
data-scroll | 启用检测 | data-scroll |
data-scroll-speed | 视差速度 | data-scroll-speed="2" |
data-scroll-direction | 视差轴 | data-scroll-direction="horizontal" |
data-scroll-sticky | 粘性定位 | data-scroll-sticky |
data-scroll-target | 粘性边界 | data-scroll-target="#section" |
data-scroll-offset | 触发偏移量 | data-scroll-offset="20%" |
data-scroll-repeat | 重复检测 | data-scroll-repeat |
data-scroll-call | 事件触发器 | data-scroll-call="myFunction" |
data-scroll-id | 唯一标识符 | data-scroll-id="hero" |
data-scroll-class | 自定义类名 | data-scroll-class="is-visible" |
import LocomotiveScroll from 'locomotive-scroll';
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true
});
<div data-scroll-container>
<div data-scroll-section>
<h1>已启用平滑滚动</h1>
</div>
</div>
<!-- 慢速视差 -->
<div data-scroll data-scroll-speed="0.5">
比滚动移动得慢(背景效果)
</div>
<!-- 快速视差 -->
<div data-scroll data-scroll-speed="3">
比滚动移动得快(前景效果)
</div>
<!-- 反向视差 -->
<div data-scroll data-scroll-speed="-2">
向相反方向移动
</div>
<!-- 水平视差 -->
<div data-scroll data-scroll-speed="2" data-scroll-direction="horizontal">
水平移动
</div>
// 追踪滚动进度
scroll.on('scroll', (args) => {
console.log(args.scroll.y); // 当前滚动位置
console.log(args.speed); // 滚动速度
console.log(args.direction); // 滚动方向
// 访问特定元素的进度
if (args.currentElements['hero']) {
const progress = args.currentElements['hero'].progress;
console.log(`Hero 进度: ${progress}`); // 0 到 1
}
});
// 调用事件
scroll.on('call', (value, way, obj) => {
console.log(`事件触发: ${value}`);
// value = data-scroll-call 属性值
// way = 'enter' 或 'exit'
// obj = {id, el}
});
<div data-scroll data-scroll-id="hero">Hero 区块</div>
<div data-scroll data-scroll-call="playVideo">视频区块</div>
<!-- 在父区块内保持粘性 -->
<div data-scroll-section>
<div data-scroll data-scroll-sticky>
当区块在视口内时,我会保持粘性
</div>
</div>
<!-- 指定目标保持粘性 -->
<div id="sticky-container">
<div data-scroll data-scroll-sticky data-scroll-target="#sticky-container">
我在 #sticky-container 内保持粘性
</div>
</div>
// 滚动到元素
scroll.scrollTo('#target-section');
// 滚动到顶部
scroll.scrollTo('top');
// 滚动到底部
scroll.scrollTo('bottom');
// 带选项的滚动
scroll.scrollTo('#target', {
offset: -100, // 偏移量(像素)
duration: 1000, // 持续时间(毫秒)
easing: [0.25, 0.0, 0.35, 1.0], // 三次贝塞尔曲线
disableLerp: true, // 禁用平滑插值
callback: () => console.log('已滚动!')
});
// 滚动到像素值
scroll.scrollTo(500);
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true,
direction: 'horizontal'
});
<div data-scroll-container>
<div data-scroll-section style="display: flex; width: 300vw;">
<div>区块 1</div>
<div>区块 2</div>
<div>区块 3</div>
</div>
</div>
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true,
// 平板设置
tablet: {
smooth: true,
breakpoint: 1024
},
// 智能手机设置
smartphone: {
smooth: false, // 在移动端禁用以提升性能
breakpoint: 768
}
});
Locomotive Scroll 和 GSAP ScrollTrigger 可协同工作以实现高级动画:
import LocomotiveScroll from 'locomotive-scroll';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
const locoScroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true
});
// 将 Locomotive Scroll 与 ScrollTrigger 同步
locoScroll.on('scroll', ScrollTrigger.update);
ScrollTrigger.scrollerProxy('[data-scroll-container]', {
scrollTop(value) {
return arguments.length
? locoScroll.scrollTo(value, 0, 0)
: locoScroll.scroll.instance.scroll.y;
},
getBoundingClientRect() {
return {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight
};
},
pinType: document.querySelector('[data-scroll-container]').style.transform
? 'transform'
: 'fixed'
});
// 使用 ScrollTrigger 的 GSAP 动画
gsap.to('.fade-in', {
scrollTrigger: {
trigger: '.fade-in',
scroller: '[data-scroll-container]',
start: 'top bottom',
end: 'top center',
scrub: true
},
opacity: 1,
y: 0
});
// 当 Locomotive 更新时更新 ScrollTrigger
ScrollTrigger.addEventListener('refresh', () => locoScroll.update());
ScrollTrigger.refresh();
const scroll = new LocomotiveScroll();
// 生命周期
scroll.init(); // 重新初始化
scroll.update(); // 刷新元素位置
scroll.destroy(); // 清理
scroll.start(); // 恢复滚动
scroll.stop(); // 暂停滚动
// 导航
scroll.scrollTo(target, options);
scroll.setScroll(x, y);
// 事件
scroll.on('scroll', callback);
scroll.on('call', callback);
scroll.off('scroll', callback);
使用 data-scroll-section 来分割长页面:
<div data-scroll-container>
<div data-scroll-section>区块 1</div>
<div data-scroll-section>区块 2</div>
<div data-scroll-section>区块 3</div>
</div>
限制视差元素数量 - 过多会影响性能
在移动端禁用 如果性能不佳:
smartphone: { smooth: false }
在调整大小时更新:
window.addEventListener('resize', () => {
scroll.update();
});
在不需要时销毁:
scroll.destroy();
问题:position: fixed 元素在平滑滚动时会失效
解决方案:改用 data-scroll-sticky 或将固定元素放在容器外:
<!-- 容器外的固定导航 -->
<nav style="position: fixed;">导航</nav>
<div data-scroll-container>
<!-- 页面内容 -->
</div>
问题:所有图片同时加载
解决方案:与懒加载集成:
<img data-scroll data-src="image.jpg" class="lazy">
scroll.on('call', (func) => {
if (func === 'lazyLoad') {
// 触发懒加载
}
});
问题:动态内容未更新滚动位置
解决方案:在 DOM 变化后调用 update():
// 添加内容后
addDynamicContent();
scroll.update();
问题:屏幕阅读器和键盘导航失效
解决方案:提供禁用选项:
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const scroll = new LocomotiveScroll({
smooth: !prefersReducedMotion
});
问题:在路由更改时未清理滚动实例(单页应用)
解决方案:在卸载时始终销毁:
// React 示例
useEffect(() => {
const scroll = new LocomotiveScroll();
return () => scroll.destroy();
}, []);
问题:视差元素重叠不正确
解决方案:在视差层上设置显式的 z-index:
[data-scroll-speed] {
position: relative;
z-index: var(--layer-depth);
}
generate_config.py - 配置生成器,integration_helper.py - GSAP 集成代码api_reference.md - 完整 API,gsap_integration.md - GSAP ScrollTrigger 模式starter_locomotive/ - 包含示例的完整入门模板每周安装量
88
代码仓库
GitHub 星标数
11
首次出现
2026年2月27日
安全审计
已安装于
opencode87
github-copilot85
codex85
amp85
cline85
kimi-cli85
Comprehensive guide for implementing smooth scrolling, parallax effects, and scroll-driven animations using Locomotive Scroll.
Locomotive Scroll is a JavaScript library that provides:
When to use Locomotive Scroll:
Trade-offs:
npm install locomotive-scroll
// ES6
import LocomotiveScroll from 'locomotive-scroll';
import 'locomotive-scroll/dist/locomotive-scroll.css';
// Or via CDN
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/locomotive-scroll/dist/locomotive-scroll.min.css">
<script src="https://cdn.jsdelivr.net/npm/locomotive-scroll/dist/locomotive-scroll.min.js"></script>
Every Locomotive Scroll implementation requires specific data attributes:
<!-- Scroll container (required) -->
<div data-scroll-container>
<!-- Scroll sections (optional, improves performance) -->
<div data-scroll-section>
<!-- Tracked elements -->
<h1 data-scroll>Basic detection</h1>
<!-- Parallax element -->
<div data-scroll data-scroll-speed="2">
Moves faster than scroll
</div>
<!-- Sticky element -->
<div data-scroll data-scroll-sticky>
Sticks within section
</div>
<!-- Element with ID for tracking -->
<div data-scroll data-scroll-id="hero">
Accessible via JavaScript
</div>
<!-- Call event trigger -->
<div data-scroll data-scroll-call="fadeIn">
Triggers custom event
</div>
</div>
</div>
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true,
lerp: 0.1, // Smoothness (0-1, lower = smoother)
multiplier: 1, // Speed multiplier
class: 'is-inview', // Class added to visible elements
repeat: false, // Repeat in-view detection
offset: [0, 0] // Global trigger offset [bottom, top]
});
| Attribute | Purpose | Example |
|---|---|---|
data-scroll | Enable detection | data-scroll |
data-scroll-speed | Parallax speed | data-scroll-speed="2" |
data-scroll-direction | Parallax axis | data-scroll-direction="horizontal" |
data-scroll-sticky |
import LocomotiveScroll from 'locomotive-scroll';
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true
});
<div data-scroll-container>
<div data-scroll-section>
<h1>Smooth scrolling enabled</h1>
</div>
</div>
<!-- Slow parallax -->
<div data-scroll data-scroll-speed="0.5">
Moves slower than scroll (background effect)
</div>
<!-- Fast parallax -->
<div data-scroll data-scroll-speed="3">
Moves faster than scroll (foreground effect)
</div>
<!-- Reverse parallax -->
<div data-scroll data-scroll-speed="-2">
Moves in opposite direction
</div>
<!-- Horizontal parallax -->
<div data-scroll data-scroll-speed="2" data-scroll-direction="horizontal">
Moves horizontally
</div>
// Track scroll progress
scroll.on('scroll', (args) => {
console.log(args.scroll.y); // Current scroll position
console.log(args.speed); // Scroll speed
console.log(args.direction); // Scroll direction
// Access specific element progress
if (args.currentElements['hero']) {
const progress = args.currentElements['hero'].progress;
console.log(`Hero progress: ${progress}`); // 0 to 1
}
});
// Call events
scroll.on('call', (value, way, obj) => {
console.log(`Event triggered: ${value}`);
// value = data-scroll-call attribute value
// way = 'enter' or 'exit'
// obj = {id, el}
});
<div data-scroll data-scroll-id="hero">Hero section</div>
<div data-scroll data-scroll-call="playVideo">Video section</div>
<!-- Stick within parent section -->
<div data-scroll-section>
<div data-scroll data-scroll-sticky>
I stick while section is in view
</div>
</div>
<!-- Stick with specific target -->
<div id="sticky-container">
<div data-scroll data-scroll-sticky data-scroll-target="#sticky-container">
I stick within #sticky-container
</div>
</div>
// Scroll to element
scroll.scrollTo('#target-section');
// Scroll to top
scroll.scrollTo('top');
// Scroll to bottom
scroll.scrollTo('bottom');
// Scroll with options
scroll.scrollTo('#target', {
offset: -100, // Offset in pixels
duration: 1000, // Duration in ms
easing: [0.25, 0.0, 0.35, 1.0], // Cubic bezier
disableLerp: true, // Disable smooth lerp
callback: () => console.log('Scrolled!')
});
// Scroll to pixel value
scroll.scrollTo(500);
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true,
direction: 'horizontal'
});
<div data-scroll-container>
<div data-scroll-section style="display: flex; width: 300vw;">
<div>Section 1</div>
<div>Section 2</div>
<div>Section 3</div>
</div>
</div>
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true,
// Tablet settings
tablet: {
smooth: true,
breakpoint: 1024
},
// Smartphone settings
smartphone: {
smooth: false, // Disable on mobile for performance
breakpoint: 768
}
});
Locomotive Scroll and GSAP ScrollTrigger work together for advanced animations:
import LocomotiveScroll from 'locomotive-scroll';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
const locoScroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true
});
// Sync Locomotive Scroll with ScrollTrigger
locoScroll.on('scroll', ScrollTrigger.update);
ScrollTrigger.scrollerProxy('[data-scroll-container]', {
scrollTop(value) {
return arguments.length
? locoScroll.scrollTo(value, 0, 0)
: locoScroll.scroll.instance.scroll.y;
},
getBoundingClientRect() {
return {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight
};
},
pinType: document.querySelector('[data-scroll-container]').style.transform
? 'transform'
: 'fixed'
});
// GSAP animation with ScrollTrigger
gsap.to('.fade-in', {
scrollTrigger: {
trigger: '.fade-in',
scroller: '[data-scroll-container]',
start: 'top bottom',
end: 'top center',
scrub: true
},
opacity: 1,
y: 0
});
// Update ScrollTrigger when Locomotive updates
ScrollTrigger.addEventListener('refresh', () => locoScroll.update());
ScrollTrigger.refresh();
const scroll = new LocomotiveScroll();
// Lifecycle
scroll.init(); // Reinitialize
scroll.update(); // Refresh element positions
scroll.destroy(); // Clean up
scroll.start(); // Resume scrolling
scroll.stop(); // Pause scrolling
// Navigation
scroll.scrollTo(target, options);
scroll.setScroll(x, y);
// Events
scroll.on('scroll', callback);
scroll.on('call', callback);
scroll.off('scroll', callback);
data-scroll-section to segment long pages:<div data-scroll-container>
<div data-scroll-section>Section 1</div>
<div data-scroll-section>Section 2</div>
<div data-scroll-section>Section 3</div>
</div>
2. Limit parallax elements - Too many can impact performance
smartphone: { smooth: false }
window.addEventListener('resize', () => {
scroll.update();
});
5. Destroy when not needed :
scroll.destroy();
Problem : position: fixed elements break with smooth scroll
Solution : Use data-scroll-sticky instead or add fixed elements outside container:
<!-- Fixed nav outside container -->
<nav style="position: fixed;">Navigation</nav>
<div data-scroll-container>
<!-- Page content -->
</div>
Problem : All images load at once
Solution : Integrate with lazy loading:
<img data-scroll data-src="image.jpg" class="lazy">
scroll.on('call', (func) => {
if (func === 'lazyLoad') {
// Trigger lazy load
}
});
Problem : Dynamic content doesn't update scroll positions
Solution : Call update() after DOM changes:
// After adding content
addDynamicContent();
scroll.update();
Problem : Screen readers and keyboard navigation broken
Solution : Provide disable option:
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const scroll = new LocomotiveScroll({
smooth: !prefersReducedMotion
});
Problem : Scroll instance not cleaned up on route changes (SPAs)
Solution : Always destroy on unmount:
// React example
useEffect(() => {
const scroll = new LocomotiveScroll();
return () => scroll.destroy();
}, []);
Problem : Parallax elements overlap incorrectly
Solution : Set explicit z-index on parallax layers:
[data-scroll-speed] {
position: relative;
z-index: var(--layer-depth);
}
generate_config.py - Configuration generator, integration_helper.py - GSAP integration codeapi_reference.md - Complete API, gsap_integration.md - GSAP ScrollTrigger patternsstarter_locomotive/ - Complete starter template with examplesWeekly Installs
88
Repository
GitHub Stars
11
First Seen
Feb 27, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode87
github-copilot85
codex85
amp85
cline85
kimi-cli85
React视图过渡API使用指南:实现原生浏览器动画与状态管理
5,700 周安装
| Sticky positioning |
data-scroll-sticky |
data-scroll-target | Sticky boundary | data-scroll-target="#section" |
data-scroll-offset | Trigger offset | data-scroll-offset="20%" |
data-scroll-repeat | Repeat detection | data-scroll-repeat |
data-scroll-call | Event trigger | data-scroll-call="myFunction" |
data-scroll-id | Unique identifier | data-scroll-id="hero" |
data-scroll-class | Custom class | data-scroll-class="is-visible" |