gsap-scrolltrigger by bbeierle12/skill-mcp-claude
npx skills add https://github.com/bbeierle12/skill-mcp-claude --skill gsap-scrolltrigger滚动驱动的动画与交互。
npm install gsap
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
gsap.to('.box', {
x: 500,
scrollTrigger: {
trigger: '.box',
start: 'top center',
end: 'bottom center',
scrub: true
}
});
gsap.to('.element', {
x: 200,
scrollTrigger: {
trigger: '.element', // 触发动画的元素
start: 'top center', // 当触发器到达视口中心时
end: 'bottom center', // 当触发器离开视口中心时
toggleActions: 'play pause resume reset'
}
});
// 格式:"触发器位置 视口位置"
start: 'top center' // 触发器的顶部到达视口中心
start: 'top 80%' // 触发器的顶部到达视口 80% 的位置
start: 'center center' // 触发器的中心到达视口中心
start: 'bottom top' // 触发器的底部到达视口顶部
start: 'top top+=100' // 触发器的顶部到达视口顶部下方 100px 处
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 值 | 描述 |
|---|---|
top | 顶部边缘 |
center | 中心 |
bottom | 底部边缘 |
80% | 距离顶部 80% |
+=100 | 加上 100 像素 |
-=50 | 减去 50 像素 |
// 动画进度与滚动位置绑定
gsap.to('.progress-bar', {
scaleX: 1,
scrollTrigger: {
trigger: '.content',
start: 'top top',
end: 'bottom bottom',
scrub: true // 直接与滚动关联
}
});
gsap.to('.element', {
x: 500,
scrollTrigger: {
trigger: '.section',
scrub: 1, // 1 秒平滑过渡
// scrub: 0.5 // 0.5 秒平滑过渡
// scrub: 2 // 2 秒平滑过渡(有滞后感)
}
});
const tl = gsap.timeline({
scrollTrigger: {
trigger: '.container',
start: 'top top',
end: '+=3000', // 滚动距离
scrub: 1,
pin: true
}
});
tl.to('.step1', { opacity: 1 })
.to('.step2', { opacity: 1 })
.to('.step3', { opacity: 1 });
ScrollTrigger.create({
trigger: '.panel',
start: 'top top',
end: '+=500', // 固定 500px 的滚动距离
pin: true
});
gsap.to('.content', {
x: '-200%',
ease: 'none',
scrollTrigger: {
trigger: '.horizontal-section',
start: 'top top',
end: () => '+=' + document.querySelector('.horizontal-section').offsetWidth,
pin: true,
scrub: 1
}
});
ScrollTrigger.create({
trigger: '.section',
pin: true,
pinSpacing: true, // 默认:为固定期间添加空间
// pinSpacing: false // 无额外空间(内容重叠)
// pinSpacing: '500px' // 自定义间距
});
// 格式:"进入时 离开时 返回进入时 返回离开时"
toggleActions: 'play pause resume reset'
// 常见组合:
toggleActions: 'play none none none' // 仅播放一次
toggleActions: 'play reverse play reverse' // 切换方向
toggleActions: 'restart none none none' // 每次重新开始
toggleActions: 'play complete reverse reset'
| 操作 | 效果 |
|---|---|
play | 向前播放 |
pause | 暂停 |
resume | 从暂停处恢复 |
reverse | 向后播放 |
restart | 从头重新开始 |
reset | 重置到开始(无动画) |
complete | 跳转到结束 |
none | 不执行任何操作 |
ScrollTrigger.create({
trigger: '.sections',
start: 'top top',
end: 'bottom bottom',
snap: 1 / 4 // 吸附到四分之一处
});
const tl = gsap.timeline({
scrollTrigger: {
trigger: '.container',
scrub: 1,
snap: {
snapTo: 'labels',
duration: 0.5,
ease: 'power2.inOut'
}
}
});
tl.addLabel('intro')
.to('.a', { opacity: 1 })
.addLabel('middle')
.to('.b', { opacity: 1 })
.addLabel('end');
snap: {
snapTo: [0, 0.25, 0.5, 0.75, 1], // 吸附到特定点
duration: { min: 0.2, max: 0.6 }, // 吸附持续时间范围
delay: 0, // 吸附前延迟
ease: 'power1.inOut', // 吸附缓动
directional: true // 按滚动方向吸附
}
ScrollTrigger.create({
trigger: '.section',
onEnter: () => console.log('已进入'),
onLeave: () => console.log('已离开'),
onEnterBack: () => console.log('从底部进入'),
onLeaveBack: () => console.log('向上离开'),
onUpdate: (self) => console.log('进度:', self.progress),
onToggle: (self) => console.log('激活:', self.isActive),
onRefresh: () => console.log('已刷新')
});
ScrollTrigger.create({
trigger: '.section',
start: 'top bottom',
end: 'bottom top',
onUpdate: (self) => {
// self.progress: 0 到 1
// self.direction: 1 (向下) 或 -1 (向上)
// self.velocity: 滚动速度
updateElement(self.progress);
}
});
// 背景移动速度慢于滚动
gsap.to('.background', {
yPercent: -50,
ease: 'none',
scrollTrigger: {
trigger: '.section',
scrub: true
}
});
// 前景移动速度快于滚动
gsap.to('.foreground', {
yPercent: 50,
ease: 'none',
scrollTrigger: {
trigger: '.section',
scrub: true
}
});
const layers = [
{ selector: '.layer-1', speed: -20 },
{ selector: '.layer-2', speed: -40 },
{ selector: '.layer-3', speed: -60 },
{ selector: '.layer-4', speed: -80 }
];
layers.forEach(layer => {
gsap.to(layer.selector, {
yPercent: layer.speed,
ease: 'none',
scrollTrigger: {
trigger: '.parallax-section',
start: 'top bottom',
end: 'bottom top',
scrub: true
}
});
});
const sections = gsap.utils.toArray('.panel');
gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: 'none',
scrollTrigger: {
trigger: '.horizontal-container',
pin: true,
scrub: 1,
snap: 1 / (sections.length - 1),
end: () => '+=' + document.querySelector('.horizontal-container').offsetWidth
}
});
ScrollTrigger.create({
trigger: '.section',
start: 'top center',
end: 'bottom center',
markers: true, // 显示视觉标记
// markers: { startColor: 'green', endColor: 'red', fontSize: '12px' }
});
ScrollTrigger.batch('.card', {
onEnter: (elements) => {
gsap.from(elements, {
opacity: 0,
y: 50,
stagger: 0.1,
duration: 0.5
});
},
start: 'top 85%'
});
ScrollTrigger.batch('.item', {
interval: 0.1, // 批次之间的时间间隔
batchMax: 3, // 每批次最大项目数
onEnter: batch => gsap.to(batch, { opacity: 1, y: 0, stagger: 0.1 }),
onLeave: batch => gsap.to(batch, { opacity: 0, y: 20 }),
onEnterBack: batch => gsap.to(batch, { opacity: 1, y: 0 }),
onLeaveBack: batch => gsap.to(batch, { opacity: 0, y: -20 })
});
gsap.utils.toArray('.reveal').forEach(elem => {
gsap.from(elem, {
opacity: 0,
y: 50,
duration: 0.8,
scrollTrigger: {
trigger: elem,
start: 'top 80%',
toggleActions: 'play none none none'
}
});
});
gsap.to('.progress-bar', {
scaleX: 1,
transformOrigin: 'left center',
ease: 'none',
scrollTrigger: {
trigger: 'body',
start: 'top top',
end: 'bottom bottom',
scrub: 0.3
}
});
ScrollTrigger.create({
start: 'top -80',
onUpdate: (self) => {
if (self.direction === 1) {
gsap.to('.header', { y: -80, duration: 0.3 });
} else {
gsap.to('.header', { y: 0, duration: 0.3 });
}
}
});
// 用户滚动时显示倒计时部分
const sections = ['days', 'hours', 'minutes', 'seconds'];
sections.forEach((section, i) => {
gsap.from(`.countdown-${section}`, {
opacity: 0,
scale: 0.8,
y: 50,
duration: 0.8,
ease: 'power3.out',
scrollTrigger: {
trigger: `.countdown-${section}`,
start: 'top 80%',
toggleActions: 'play none none none'
}
});
});
gsap.timeline({
scrollTrigger: {
trigger: '.time-section',
start: 'top center',
end: 'bottom center',
scrub: 1
}
})
.to('.time-digit', {
textShadow: '0 0 50px #00F5FF, 0 0 100px #00F5FF',
scale: 1.1
})
.to('.particles', {
opacity: 1,
filter: 'blur(0px)'
}, '<');
// 在移动端需要时禁用
ScrollTrigger.matchMedia({
'(min-width: 768px)': function() {
// 桌面端动画
},
'(max-width: 767px)': function() {
// 简化的移动端动画
}
});
// 调整大小时刷新
ScrollTrigger.refresh();
// 终止所有 ScrollTrigger
ScrollTrigger.killAll();
// 终止特定触发器
const st = ScrollTrigger.create({ ... });
st.kill();
gsap-fundamentals 了解动画基础gsap-sequencing 了解时间轴组合gsap-react 了解 React 与 ScrollTrigger 的集成每周安装量
284
仓库
GitHub 星标
7
首次出现
2026年1月22日
安全审计
安装于
opencode251
gemini-cli249
codex243
github-copilot231
cursor208
claude-code180
Scroll-driven animations and interactions.
npm install gsap
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
gsap.to('.box', {
x: 500,
scrollTrigger: {
trigger: '.box',
start: 'top center',
end: 'bottom center',
scrub: true
}
});
gsap.to('.element', {
x: 200,
scrollTrigger: {
trigger: '.element', // Element that triggers the animation
start: 'top center', // When trigger hits viewport center
end: 'bottom center', // When trigger leaves viewport center
toggleActions: 'play pause resume reset'
}
});
// Format: "trigger-position viewport-position"
start: 'top center' // Trigger's top hits viewport center
start: 'top 80%' // Trigger's top hits 80% down viewport
start: 'center center' // Trigger's center hits viewport center
start: 'bottom top' // Trigger's bottom hits viewport top
start: 'top top+=100' // Trigger's top hits 100px below viewport top
| Value | Description |
|---|---|
top | Top edge |
center | Center |
bottom | Bottom edge |
80% | 80% from top |
+=100 | Plus 100 pixels |
-=50 | Minus 50 pixels |
// Animation progress tied to scroll position
gsap.to('.progress-bar', {
scaleX: 1,
scrollTrigger: {
trigger: '.content',
start: 'top top',
end: 'bottom bottom',
scrub: true // Directly linked to scroll
}
});
gsap.to('.element', {
x: 500,
scrollTrigger: {
trigger: '.section',
scrub: 1, // 1 second smoothing
// scrub: 0.5 // 0.5 second smoothing
// scrub: 2 // 2 second smoothing (laggy feel)
}
});
const tl = gsap.timeline({
scrollTrigger: {
trigger: '.container',
start: 'top top',
end: '+=3000', // Scroll distance
scrub: 1,
pin: true
}
});
tl.to('.step1', { opacity: 1 })
.to('.step2', { opacity: 1 })
.to('.step3', { opacity: 1 });
ScrollTrigger.create({
trigger: '.panel',
start: 'top top',
end: '+=500', // Pin for 500px of scroll
pin: true
});
gsap.to('.content', {
x: '-200%',
ease: 'none',
scrollTrigger: {
trigger: '.horizontal-section',
start: 'top top',
end: () => '+=' + document.querySelector('.horizontal-section').offsetWidth,
pin: true,
scrub: 1
}
});
ScrollTrigger.create({
trigger: '.section',
pin: true,
pinSpacing: true, // Default: adds space for pinned duration
// pinSpacing: false // No extra space (content overlaps)
// pinSpacing: '500px' // Custom spacing
});
// Format: "onEnter onLeave onEnterBack onLeaveBack"
toggleActions: 'play pause resume reset'
// Common combinations:
toggleActions: 'play none none none' // Play once
toggleActions: 'play reverse play reverse' // Toggle direction
toggleActions: 'restart none none none' // Restart each time
toggleActions: 'play complete reverse reset'
| Action | Effect |
|---|---|
play | Play forward |
pause | Pause |
resume | Resume from paused |
reverse | Play backward |
restart | Restart from beginning |
reset | Reset to start (no animation) |
ScrollTrigger.create({
trigger: '.sections',
start: 'top top',
end: 'bottom bottom',
snap: 1 / 4 // Snap to quarters
});
const tl = gsap.timeline({
scrollTrigger: {
trigger: '.container',
scrub: 1,
snap: {
snapTo: 'labels',
duration: 0.5,
ease: 'power2.inOut'
}
}
});
tl.addLabel('intro')
.to('.a', { opacity: 1 })
.addLabel('middle')
.to('.b', { opacity: 1 })
.addLabel('end');
snap: {
snapTo: [0, 0.25, 0.5, 0.75, 1], // Snap to specific points
duration: { min: 0.2, max: 0.6 }, // Snap duration range
delay: 0, // Delay before snap
ease: 'power1.inOut', // Snap easing
directional: true // Snap in scroll direction
}
ScrollTrigger.create({
trigger: '.section',
onEnter: () => console.log('Entered'),
onLeave: () => console.log('Left'),
onEnterBack: () => console.log('Entered from bottom'),
onLeaveBack: () => console.log('Left going up'),
onUpdate: (self) => console.log('Progress:', self.progress),
onToggle: (self) => console.log('Active:', self.isActive),
onRefresh: () => console.log('Refreshed')
});
ScrollTrigger.create({
trigger: '.section',
start: 'top bottom',
end: 'bottom top',
onUpdate: (self) => {
// self.progress: 0 to 1
// self.direction: 1 (down) or -1 (up)
// self.velocity: scroll speed
updateElement(self.progress);
}
});
// Background moves slower than scroll
gsap.to('.background', {
yPercent: -50,
ease: 'none',
scrollTrigger: {
trigger: '.section',
scrub: true
}
});
// Foreground moves faster
gsap.to('.foreground', {
yPercent: 50,
ease: 'none',
scrollTrigger: {
trigger: '.section',
scrub: true
}
});
const layers = [
{ selector: '.layer-1', speed: -20 },
{ selector: '.layer-2', speed: -40 },
{ selector: '.layer-3', speed: -60 },
{ selector: '.layer-4', speed: -80 }
];
layers.forEach(layer => {
gsap.to(layer.selector, {
yPercent: layer.speed,
ease: 'none',
scrollTrigger: {
trigger: '.parallax-section',
start: 'top bottom',
end: 'bottom top',
scrub: true
}
});
});
const sections = gsap.utils.toArray('.panel');
gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: 'none',
scrollTrigger: {
trigger: '.horizontal-container',
pin: true,
scrub: 1,
snap: 1 / (sections.length - 1),
end: () => '+=' + document.querySelector('.horizontal-container').offsetWidth
}
});
ScrollTrigger.create({
trigger: '.section',
start: 'top center',
end: 'bottom center',
markers: true, // Show visual markers
// markers: { startColor: 'green', endColor: 'red', fontSize: '12px' }
});
ScrollTrigger.batch('.card', {
onEnter: (elements) => {
gsap.from(elements, {
opacity: 0,
y: 50,
stagger: 0.1,
duration: 0.5
});
},
start: 'top 85%'
});
ScrollTrigger.batch('.item', {
interval: 0.1, // Time between batches
batchMax: 3, // Max items per batch
onEnter: batch => gsap.to(batch, { opacity: 1, y: 0, stagger: 0.1 }),
onLeave: batch => gsap.to(batch, { opacity: 0, y: 20 }),
onEnterBack: batch => gsap.to(batch, { opacity: 1, y: 0 }),
onLeaveBack: batch => gsap.to(batch, { opacity: 0, y: -20 })
});
gsap.utils.toArray('.reveal').forEach(elem => {
gsap.from(elem, {
opacity: 0,
y: 50,
duration: 0.8,
scrollTrigger: {
trigger: elem,
start: 'top 80%',
toggleActions: 'play none none none'
}
});
});
gsap.to('.progress-bar', {
scaleX: 1,
transformOrigin: 'left center',
ease: 'none',
scrollTrigger: {
trigger: 'body',
start: 'top top',
end: 'bottom bottom',
scrub: 0.3
}
});
ScrollTrigger.create({
start: 'top -80',
onUpdate: (self) => {
if (self.direction === 1) {
gsap.to('.header', { y: -80, duration: 0.3 });
} else {
gsap.to('.header', { y: 0, duration: 0.3 });
}
}
});
// Reveal countdown sections as user scrolls
const sections = ['days', 'hours', 'minutes', 'seconds'];
sections.forEach((section, i) => {
gsap.from(`.countdown-${section}`, {
opacity: 0,
scale: 0.8,
y: 50,
duration: 0.8,
ease: 'power3.out',
scrollTrigger: {
trigger: `.countdown-${section}`,
start: 'top 80%',
toggleActions: 'play none none none'
}
});
});
gsap.timeline({
scrollTrigger: {
trigger: '.time-section',
start: 'top center',
end: 'bottom center',
scrub: 1
}
})
.to('.time-digit', {
textShadow: '0 0 50px #00F5FF, 0 0 100px #00F5FF',
scale: 1.1
})
.to('.particles', {
opacity: 1,
filter: 'blur(0px)'
}, '<');
// Disable on mobile if needed
ScrollTrigger.matchMedia({
'(min-width: 768px)': function() {
// Desktop animations
},
'(max-width: 767px)': function() {
// Simpler mobile animations
}
});
// Refresh on resize
ScrollTrigger.refresh();
// Kill all ScrollTriggers
ScrollTrigger.killAll();
// Kill specific trigger
const st = ScrollTrigger.create({ ... });
st.kill();
gsap-fundamentals for animation basicsgsap-sequencing for timeline compositiongsap-react for React integration with ScrollTriggerWeekly Installs
284
Repository
GitHub Stars
7
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode251
gemini-cli249
codex243
github-copilot231
cursor208
claude-code180
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
106,200 周安装
complete| Jump to end |
none | Do nothing |