、
视频部分 需要 改成 本地视频
// 注册ScrollTrigger插件
gsap.registerPlugin(ScrollTrigger);
// 英雄区域入场动画
gsap.timeline()
.from(".hero-title", {
opacity: 0, // 初始完全透明
y: 50, // 初始位置在下方50px
duration: 1, // 动画持续1秒
ease: "power2.out" // 缓动效果:快速开始,缓慢结束
})
.from(".hero-subtitle", {
opacity: 0, // 初始完全透明
y: 30, // 初始位置在下方30px
duration: 1, // 动画持续1秒
ease: "power2.out" // 缓动效果:快速开始,缓慢结束
}, "-=0.5"); // 提前0.5秒开始,与前一个动画重叠
// 英雄区域退场动画
ScrollTrigger.create({
trigger: ".hero", // 触发元素
start: "top top", // 开始位置:元素顶部对齐视窗顶部
end: "bottom top", // 结束位置:元素底部对齐视窗顶部
scrub: 1, // 平滑滚动效果,值为1表示滚动响应的延迟程度
pin: true, // 固定触发元素
onUpdate: (self) => { // 滚动更新时的回调函数
gsap.to(".hero-title", {
opacity: 1 - self.progress, // 透明度随进度反向变化
x: -100 * self.progress, // 向左移动大100px
duration: 0 // 立即更新无过渡
});
gsap.to(".hero-subtitle", {
opacity: 1 - self.progress, // 透明度随进度反向变化
x: 100 * self.progress, // 向右移动,最大100px
duration: 0 // 立即更新,无过渡
});
}
});
// 视频区域动画
ScrollTrigger.create({
trigger: ".video-section", // 触发元素:指定哪个元素触发动画
start: "top top", // 开始位置:元素顶部对齐视窗顶部时触发
end: "+=200%", // 结束位置:从开始位置再往下滚动两个视窗高度
pin: true, // 固定元素:在动画期间将元素固定在视窗中
scrub: true, // 滚动同步:动画进度跟随滚动条移动,true表示有平滑效果
markers: true, // 显示标记:在开发时显示滚动触发的位置标记
onUpdate: self => { // 滚动更新时的回调函数
const progress = self.progress; // 获取滚动进度(0-1之间)
const video = document.querySelector("#myVideo");
if (progress <= 0.5) { // 前半段:视频播放(0-50%的滚动进度)
if (video.duration) {
// 视频播放进度跟随滚动进度,progress * 2将0-0.5映射为0-1
video.currentTime = (progress * 2) * video.duration;
}
} else { // 后半段:文字动画(50-100%的滚动进度)
// 将0.5-1的进度映射为0-1,用于文字动画
const textProgress = (progress - 0.5) * 2;
// 左侧文字动画
gsap.to(".video-text-left", {
x: 100 * progress * 2, // 水平移动距离随进度变化
opacity: textProgress, // 透明度随进度变化
duration: 0, // 立即更新,无过渡动画
ease: "power2.inOut" // 缓动效果:渐入渐出
});
// 右侧文字动画
gsap.to(".video-text-right", {
x: -100 * progress * 2, // 反方向水平移动
opacity: textProgress, // 透明度随进度变化
duration: 0, // 立即更新,无过渡动画
ease: "power2.inOut" // 缓动效果:渐入渐出
});
}
}
});
// 文字滑入区域动画
const slidingTexts = document.querySelectorAll('.sliding-text');
// 设置文字初始状态
gsap.set(slidingTexts, {
opacity: 0, // 初始完全透明
x: (index) => index % 2 === 0 ? -100 : 100, // 奇数从左侧,偶数从右侧开始
y: 30 // 初始位置在下方30px
});
// 文字区域滚动触发器
ScrollTrigger.create({
trigger: ".text-section", // 触发元素
start: "top top", // 开始位置:元素顶部对齐视窗顶部
end: "+=100%", // 结束位置:滚动一个视窗高度
pin: true, // 固定触发元素
scrub: 1, // 平滑滚动效果
markers: true, // 显示标记(调试用)
onUpdate: (self) => {
const progress = self.progress;
slidingTexts.forEach((text, index) => {
const start = (index / slidingTexts.length);
const end = ((index + 1) / slidingTexts.length);
const textProgress = gsap.utils.clamp(
0,
1,
(progress - start) / (end - start)
);
gsap.to(text, {
opacity: textProgress, // 透明度随进度变化
x: index % 2 === 0
? -100 + (textProgress * 100) // 从左侧进入
: 100 - (textProgress * 100), // 从右侧进入
y: 30 - (textProgress * 30), // 向上移动
duration: 0, // 立即更新
ease: "power2.inOut" // 缓动效果:渐入渐出
});
});
}
});
// 图片画廊动画
const gallerySection = document.querySelector('.gallery-section');
const galleryItems = document.querySelectorAll('.gallery-item');
// 设置初始状态
gsap.set(galleryItems, {
opacity: 0,
y: 50
});
// 创建滚动触发动画
ScrollTrigger.create({
trigger: ".gallery-section", // 触发元素
start: "top top", // 开始位置:元素顶部对齐视窗顶部
end: "+=100%", // 结束位置:滚动一个视窗高度
pin: true, // 固定触发元素
scrub: 1, // 平滑滚动效果
markers: true, // 显示标记(调试用)
onUpdate: (self) => {
const progress = self.progress;
galleryItems.forEach((item, index) => {
const start = (index / galleryItems.length);
const end = ((index + 1) / galleryItems.length);
const itemProgress = gsap.utils.clamp(
0,
1,
(progress - start) / (end - start)
);
gsap.to(item, {
opacity: itemProgress, // 透明度随进度变化
y: 50 - (itemProgress * 50), // 从下方向上移动
duration: 0, // 立即更新
ease: "power2.inOut" // 缓动效果:渐入渐出
});
});
}
});
// 卡片区域动画
const cards = document.querySelectorAll('.card');
// 设置初始状态
gsap.set(cards, {
opacity: 0,
y: 30,
scale: 0.95
});
// 创建卡片动画
ScrollTrigger.create({
trigger: ".section", // 触发元素
start: "top top", // 开始位置:元素顶部对齐视窗顶部
end: "+=100%", // 结束位置:滚动一个视窗高度
pin: true, // 固定触发元素
scrub: 1, // 平滑滚动效果
markers: true, // 显示标记(调试用)
onUpdate: (self) => {
const progress = self.progress;
cards.forEach((card, index) => {
const start = (index / cards.length);
const end = ((index + 1) / cards.length);
const cardProgress = gsap.utils.clamp(
0,
1,
(progress - start) / (end - start)
);
gsap.to(card, {
opacity: cardProgress, // 透明度随进度变化
y: 30 - (cardProgress * 30), // 从下方向上移动
scale: 0.95 + (cardProgress * 0.05), // 缓慢放大
duration: 0, // 立即更新
ease: "power2.inOut" // 缓动效果:渐入渐出
});
});
}
});
// 进度条动画
gsap.to(".progress", {
width: "100%", // 宽度变化到100%
ease: "none", // 线性变化,无缓动效果
scrollTrigger: {
trigger: "body", // 触发元素是整个页面
start: "top top", // 开始位置:面顶部对齐视窗顶部
end: "bottom bottom", // 结束位置:页面底部对齐视窗底部
scrub: 0.3 // 平滑滚动效果,值较小表示响应更快
}
});