
本文详解如何使用 GSAP + ScrollTrigger 实现基于滚动位置的标题平移+渐隐动画,重点解析 opacity 插值范围、scrub 模式差异、end 动态计算及常见精度陷阱,助你避免“只淡到50%”等典型问题。
本文详解如何使用 gsap + scrolltrigger 实现基于滚动位置的标题平移+渐隐动画,重点解析 `opacity` 插值范围、`scrub` 模式差异、`end` 动态计算及常见精度陷阱,助你避免“只淡到50%”等典型问题。
在构建现代网页动效时,滚动触发的视差与渐隐组合(如横向滑动标题后垂直淡出)已成为高端品牌站与作品集的标配。GSAP 凭借其时间轴精确性与 ScrollTrigger 的声明式绑定能力,成为实现此类效果的首选方案——但若对动画生命周期与插值逻辑理解不足,极易陷入“动画卡顿”“不完全淡出”“响应式失效”等陷阱。
? 核心问题定位:为什么 opacity 只到 0.5?
原代码中 gsap.fromTo('.titles', {opacity: 1}, {opacity: 0}) 在 scrub: 0 下实际执行的是一次性非同步动画,而非随滚动实时插值。更关键的是:ScrollTrigger 的 end 值 140% 未与 .titleWrapper 的实际滚动距离对齐,导致动画仅执行了约一半进度。而所谓“将 opacity 设为 -2.5”的解决方式,实则是误打误撞利用了 GSAP 的超出范围插值容错机制(opacity
✅ 正确解法应聚焦三点:
-
动画类型匹配:淡出需
scrub: true(或数值)实现滚动同步; -
端点精准计算:
end应基于容器可滚动高度,而非固定百分比; -
属性归一化:确保
fromTo的起止值严格对应0 → 1或1 → 0。
✅ 推荐实现(Vue/HTML 环境)
// 1. 获取真实滚动边界(响应式安全)
const titleWrapper = document.querySelector('.titleWrapper');
const titles = document.querySelector('.titles');
const getScrollWidth = () => titles.scrollWidth - window.innerWidth;
// 2. 横向平移动画(带 scrub 同步)
const xTween = gsap.to(titles, {
x: () => -getScrollWidth(),
duration: 1,
ease: 'none'
});
// 3. 淡出动画(关键:使用 fromTo + scrub + 精确 end)
const fadeTween = gsap.fromTo(titles, {
opacity: 1,
ease: 'none'
}, {
opacity: 0,
ease: 'none'
});
// 4. ScrollTrigger 配置(双触发,单 timeline 更佳)
ScrollTrigger.create({
trigger: titleWrapper,
start: 'top top',
end: () => `+=${getScrollWidth()}`, // 与平移距离一致
pin: true,
animation: xTween,
scrub: 1,
invalidateOnRefresh: true
});
ScrollTrigger.create({
trigger: titleWrapper,
start: 'top 50%', // 淡出起始点:标题中部进入视口
end: () => `+=${getScrollWidth() * 0.8}`, // 淡出持续距离(可调)
animation: fadeTween,
scrub: true, // 必须启用!否则不随滚动实时变化
invalidateOnRefresh: true
});
⚠️ 关键注意事项
-
scrub是生命线:scrub: 0= 普通定时器动画(仅播放一次);scrub: true或scrub: 1= 滚动像素级同步,淡出必须启用; -
避免
opacity: -2.5这类“魔法数字”:它依赖 GSAP 内部 clamp 行为,破坏可维护性且在 SSR/SSG 场景下可能异常; -
响应式重算:务必设置
invalidateOnRefresh: true,并在window.resize时调用ScrollTrigger.refresh(); -
性能优化:对
.titles使用will-change: transform, opacity,并确保其父容器transform: none(避免层叠上下文干扰); -
调试技巧:开启
markers: true可视化触发区域,配合ScrollTrigger.debug()查看实时状态。
? 进阶建议:合并为单 Timeline(更可控)
const tl = gsap.timeline({
scrollTrigger: {
trigger: titleWrapper,
start: 'top top',
end: () => `+=${getScrollWidth()}`,
pin: true,
scrub: 1,
invalidateOnRefresh: true
}
});
tl.to(titles, { x: () => -getScrollWidth(), duration: 1, ease: 'none' })
.to(titles, { opacity: 0, duration: 0.8, ease: 'none' }, '<p>此写法将平移与淡出耦合于同一时间轴,时序绝对精准,且 <code>' 语法确保淡出与平移同步启动,彻底规避多 Trigger 引发的竞态问题。</code></p><p>掌握 GSAP 的滚动动画,本质是理解 <strong>“滚动位置 ↔ 时间轴进度 ↔ 属性插值”</strong> 的三角映射关系。摒弃试错式调整,回归配置语义化与数学一致性,你就能稳定输出影院级动效体验。</p>










