Timeline 时间轴
Timeline 是 GSAP 的核心功能之一,用于管理多个动画序列,创建复杂的动画时间线。
📚 Timeline 基础
为什么需要 Timeline?
当需要创建多个动画序列时,使用 Timeline 可以:
- 精确控制时间:控制动画的执行顺序和时机
- 统一管理:所有动画在一个时间轴上管理
- 易于控制:可以统一暂停、播放、反向等操作
- 灵活编排:支持复杂的动画序列
创建 Timeline
// 创建时间轴
const tl = gsap.timeline();
// 添加动画
tl.to(".box1", { x: 100, duration: 1 })
.to(".box2", { y: 100, duration: 1 })
.to(".box3", { rotation: 360, duration: 1 });🎯 Timeline 基本用法
顺序执行(默认)
const tl = gsap.timeline();
// 动画按顺序执行
tl.to(".box1", { x: 100, duration: 1 }) // 0-1 秒
.to(".box2", { y: 100, duration: 1 }) // 1-2 秒
.to(".box3", { rotation: 360, duration: 1 }); // 2-3 秒同时执行(使用位置参数)
const tl = gsap.timeline();
// 所有动画同时开始
tl.to(".box1", { x: 100, duration: 1 }, 0) // 从 0 秒开始
.to(".box2", { y: 100, duration: 1 }, 0) // 从 0 秒开始
.to(".box3", { rotation: 360, duration: 1 }, 0); // 从 0 秒开始相对位置
const tl = gsap.timeline();
// 使用相对位置
tl.to(".box1", { x: 100, duration: 1 })
.to(".box2", { y: 100, duration: 1 }, "-=0.5") // 提前 0.5 秒开始
.to(".box3", { rotation: 360, duration: 1 }, "+=0.5"); // 延迟 0.5 秒开始位置标签(Labels)
const tl = gsap.timeline();
// 添加标签
tl.to(".box1", { x: 100, duration: 1 })
.addLabel("middle") // 添加标签
.to(".box2", { y: 100, duration: 1 })
.to(".box3", { rotation: 360, duration: 1 }, "middle"); // 从 middle 标签位置开始🎨 Timeline 配置选项
默认配置
const tl = gsap.timeline({
paused: true, // 创建时暂停
repeat: 2, // 重复 2 次
yoyo: true, // 往返动画
delay: 1, // 延迟 1 秒
defaults: { // 默认值
duration: 1,
ease: "power2.out"
}
});常用配置
const tl = gsap.timeline({
// 时间控制
paused: false, // 是否暂停
delay: 0, // 延迟时间
repeat: 0, // 重复次数(-1 为无限)
yoyo: false, // 是否往返
repeatDelay: 0, // 重复之间的延迟
// 默认值
defaults: {
duration: 1,
ease: "power2.out"
},
// 回调
onComplete: function() {
console.log("完成");
},
onStart: function() {
console.log("开始");
}
});🎬 Timeline 控制方法
基本控制
const tl = gsap.timeline();
// 播放
tl.play();
// 暂停
tl.pause();
// 恢复
tl.resume();
// 反向播放
tl.reverse();
// 重新开始
tl.restart();
// 跳转到指定时间
tl.seek(1.5); // 跳转到 1.5 秒
// 跳转到指定进度
tl.progress(0.5); // 跳转到 50% 进度
// 跳转到标签
tl.seek("middle"); // 跳转到 middle 标签位置时间缩放
// 设置播放速度
tl.timeScale(0.5); // 慢速播放(0.5 倍速)
tl.timeScale(2); // 快速播放(2 倍速)
tl.timeScale(1); // 正常速度获取状态
// 是否正在播放
tl.isActive();
// 当前进度(0-1)
tl.progress();
// 当前时间
tl.time();
// 总时长
tl.duration();
// 是否已反转
tl.reversed();🎯 位置参数详解
绝对位置
const tl = gsap.timeline();
tl.to(".box1", { x: 100, duration: 1 }, 0) // 从 0 秒开始
.to(".box2", { y: 100, duration: 1 }, 1) // 从 1 秒开始
.to(".box3", { rotation: 360, duration: 1 }, 2); // 从 2 秒开始相对位置
const tl = gsap.timeline();
tl.to(".box1", { x: 100, duration: 1 })
.to(".box2", { y: 100, duration: 1 }, "-=0.5") // 提前 0.5 秒
.to(".box3", { rotation: 360, duration: 1 }, "+=0.5"); // 延迟 0.5 秒位置标签
const tl = gsap.timeline();
tl.to(".box1", { x: 100, duration: 1 })
.addLabel("start")
.to(".box2", { y: 100, duration: 1 })
.addLabel("middle")
.to(".box3", { rotation: 360, duration: 1 }, "middle"); // 从 middle 开始相对标签位置
const tl = gsap.timeline();
tl.to(".box1", { x: 100, duration: 1 })
.addLabel("middle")
.to(".box2", { y: 100, duration: 1 }, "middle+=0.5") // middle 标签后 0.5 秒
.to(".box3", { rotation: 360, duration: 1 }, "middle-=0.2"); // middle 标签前 0.2 秒🎨 实战案例
案例 1:页面加载动画
const tl = gsap.timeline();
// 依次显示元素
tl.from(".header", { y: -50, opacity: 0, duration: 0.5 })
.from(".nav-item", { x: -20, opacity: 0, duration: 0.3, stagger: 0.1 }, "-=0.3")
.from(".hero-title", { y: 50, opacity: 0, duration: 0.6 }, "-=0.2")
.from(".hero-subtitle", { y: 30, opacity: 0, duration: 0.5 }, "-=0.4")
.from(".cta-button", { scale: 0, opacity: 0, duration: 0.4, ease: "back.out(1.7)" }, "-=0.2");案例 2:卡片翻转效果
const cards = document.querySelectorAll(".card");
cards.forEach(card => {
const tl = gsap.timeline({ paused: true });
tl.to(card, { rotationY: 90, duration: 0.3 })
.set(card, { z: -100 }) // 切换内容
.to(card, { rotationY: 0, duration: 0.3 });
card.addEventListener("click", () => {
tl.play();
});
});案例 3:进度条动画
function animateProgress(percent) {
const tl = gsap.timeline();
tl.to(".progress-bar", {
width: `${percent}%`,
duration: 1,
ease: "power2.out"
})
.to(".progress-text", {
textContent: `${percent}%`,
duration: 0.5,
snap: { textContent: 1 } // 整数显示
}, "-=0.5");
}案例 4:模态框动画序列
function showModal() {
const tl = gsap.timeline();
// 背景遮罩
tl.fromTo(".modal-backdrop",
{ opacity: 0 },
{ opacity: 1, duration: 0.3 }
)
// 模态框内容
.fromTo(".modal",
{
opacity: 0,
scale: 0.8,
y: 50
},
{
opacity: 1,
scale: 1,
y: 0,
duration: 0.4,
ease: "back.out(1.7)"
},
"-=0.2" // 提前 0.2 秒开始
)
// 内容元素依次出现
.from(".modal-header", { y: -20, opacity: 0, duration: 0.3 }, "-=0.2")
.from(".modal-body", { y: 20, opacity: 0, duration: 0.3 }, "-=0.1")
.from(".modal-footer", { y: 20, opacity: 0, duration: 0.3 }, "-=0.1");
}案例 5:时间轴嵌套
// 主时间轴
const mainTl = gsap.timeline();
// 创建子时间轴
const headerTl = gsap.timeline();
headerTl.from(".logo", { scale: 0, duration: 0.5 })
.from(".nav", { x: -100, duration: 0.5 }, "-=0.3");
const contentTl = gsap.timeline();
contentTl.from(".title", { y: 50, opacity: 0, duration: 0.6 })
.from(".content", { y: 30, opacity: 0, duration: 0.5 }, "-=0.3");
// 将子时间轴添加到主时间轴
mainTl.add(headerTl)
.add(contentTl, "-=0.2"); // 提前 0.2 秒开始💡 最佳实践
1. 使用 defaults 减少重复代码
const tl = gsap.timeline({
defaults: {
duration: 0.5,
ease: "power2.out"
}
});
// 不需要重复指定 duration 和 ease
tl.to(".box1", { x: 100 })
.to(".box2", { y: 100 })
.to(".box3", { rotation: 360 });2. 使用标签提高可读性
const tl = gsap.timeline();
tl.addLabel("start")
.to(".box1", { x: 100, duration: 1 })
.addLabel("middle")
.to(".box2", { y: 100, duration: 1 })
.addLabel("end")
.to(".box3", { rotation: 360, duration: 1 });
// 可以轻松跳转到特定位置
tl.seek("middle");3. 合理使用相对位置
// ✅ 推荐:使用相对位置,易于调整
tl.to(".box1", { x: 100, duration: 1 })
.to(".box2", { y: 100, duration: 1 }, "-=0.5");
// ❌ 不推荐:使用绝对位置,难以维护
tl.to(".box1", { x: 100, duration: 1 }, 0)
.to(".box2", { y: 100, duration: 1 }, 0.5);4. 及时清理时间轴
// 保存时间轴引用
const tl = gsap.timeline();
// 组件卸载时清理
function cleanup() {
tl.kill();
}