当前位置: 首页 > news >正文

用 GSAP + ScrollTrigger 打造沉浸式视频滚动动画

页面打开时先播放一段全屏视频,随着用户下滑,文字逐渐浮现,紧接着大标题和图片内容缓缓登场,视差滚动效果可以提升用户体验和视觉冲击力的。

这个效果主要通过以下步骤实现:

  1. 初始设置:使用GSAP设置元素的初始状态(位置、透明度等)

  2. 创建时间轴:使用GSAP Timeline定义动画序列和时序

  3. 滚动触发:使用ScrollTrigger将动画与页面滚动绑定

  4. 动画序列

    • 顶部文字向下移动进入视图

    • 底部文字向上移动并渐显

    • 缩放区域逐渐显示并缩小到正常大小

    • 标题向上移动创造视觉冲击

核心在于 ScrollTrigger.create() 和 时间轴 (gsap.timeline) 的配合。

  • gsap.timeline:负责定义动画本身(比如视频透明度变化、文字上移、大标题缩放、图片进场等)。它就像一条“动画脚本”,规定了顺序和节奏。如果没有时间轴,动画就是单点触发,缺少连贯性。
  • ScrollTrigger.create():负责把这个时间轴和用户滚动绑定在一起。它控制了动画什么时候开始 (start)、什么时候结束 (end)、是否跟随滚动进度 (scrub),以及固定住元素 (pin)。它就像“驾驶员”,根据滚动条的进度来驱动时间轴。

<style>.section-Index2 {overflow: hidden;}#shopify-section-{{section.id}} .section_Index2All {background: {{ section.settings.background }};margin-top: {{section.settings.Desktop_top}}px;margin-bottom: {{section.settings.Desktop_bottom}}px;padding-top: {{section.settings.Desktop_paddingTop}}px;padding-bottom: {{section.settings.Desktop_paddingBottom}}px;}@media(max-width:999px) {#shopify-section-{{section.id}} .section_Index2All {margin-top: {{section.settings.Mobile_top}}px;margin-bottom: {{section.settings.Mobile_bottom}}px;padding-top: {{section.settings.Mobile_paddingTop}}px;padding-bottom: {{section.settings.Mobile_paddingBottom}}px;}}.videoContainer {width: 100vw;height: 100vh;overflow: hidden;position: relative;}#nebula-scroll-section {position: relative;height: 100vh;}.background-video,.background-image {width: 100%;height: 100%;object-fit: cover;}.video-overlay {position: absolute;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0,0,0,0.5);}/* 第一屏:视频和上下文字 */.videoContainer_top {width: 100%;height: 100%;opacity: 1;display: flex;align-items: center;justify-content: center;flex-direction: column;position: relative;z-index: 1;}.top-text {width: 100%;text-align: center;transform: translateY(-96px);color: white;z-index: 2;}.bottom-text {width: 100%;text-align: center;transform: translateY(206px);color: white;z-index: 2;opacity: 0;position: absolute;}/* 第二屏:白底大字 */.videoContainer_middle {position: absolute;inset: 0;width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;background: #fff;z-index: 3;mix-blend-mode: color-dodge;}.videoContainer_middle .zoom-title {width: 100%;height: 100%;overflow: hidden;display: flex;align-items: center;justify-content: center;letter-spacing: 0px;font-size: 200px;line-height: 234px;font-weight: bold;}.zoom-inner-top{position: absolute;top: 20%;z-index: 4;left: 50%;transform: translate(-50%, 0%);max-height: 380px;overflow: hidden;}/* .zoom-inner-top .background-image{max-height: 380px;} */.zoom-inner-bottom{z-index: 4;color: var(--my-text-01);position: absolute;bottom:30px;width: 100%;}.zoom-inner-bottom-inner{display: flex;flex-direction: column;max-width: 1000px;width: 100%;white-space: normal;word-break: break-word;overflow-wrap: break-word;text-align: center;gap: 12px;align-items: center;justify-content: center;margin: 0 auto;}.zoom-inner-bottom .zoom-inner-bottom-title{font-size: 36px;line-height: 1.3;font-weight: 600;}.zoom-inner-bottom .zoom-inner-bottom-text{/* margin-bottom: 12px; */}.zoom-inner-bottom .zoom-inner-bottom-svg{margin: 24px 0;}@media(max-width: 1200px) {.videoContainer_middle .zoom-title {font-size: 120px;}}@media screen and (max-width: 768px) {.videoContainer_middle .zoom-title{line-height:75px ;font-size: 72px;text-align: center;}.zoom-inner-bottom .zoom-inner-bottom-title{font-size: 24px;line-height: 32px;font-weight: 600;gap: 8px;letter-spacing: 0.3px;}.zoom-inner-bottom .zoom-inner-bottom-svg{margin: 16px 0;}.zoom-inner-bottom{bottom: 24px;}.zoom-inner-top{height: 300px;}}
</style><div class="section_Index2All"><div class="videoContainer" id="nebula-scroll-section"><!-- 第一屏:视频和文字 --><div class="videoContainer_top"><div class="top-text animate-text my_h_26 crociris_title">{{ section.settings.title }}</div><div class="video-wrapper">{% if section.settings.video != blank %}<native-video class="pc background-video">{{- section.settings.video | video_tag: autoplay: true, playsinline: true, muted: true, loop: true -}}</native-video>{% elsif section.settings.img != blank %}<imgsrc="{{ section.settings.img | image_url: width:'1920x' }}"alt="{{ section.settings.img.alt }}"class="pc background-image">{% endif %}{% if section.settings.m_video != blank %}<native-video class="mobile background-video">{{- section.settings.m_video | video_tag: autoplay: true, playsinline: true, muted: true, loop: true -}}</native-video>{% elsif section.settings.img_mobile != blank %}<imgsrc="{{ section.settings.img_mobile | image_url: width:'750x' }}"alt="{{ section.settings.img_mobile.alt }}"class="mobile background-image">{% endif %}<div class="video-overlay"></div></div><div class="bottom-text animate-text my_h_14 font-weight-400">{{ section.settings.content }}</div></div><div class="zoom-inner-top">{% if section.settings.img_1 != blank %}<imgsrc="{{ section.settings.img_1 | image_url: width:'1920x' }}"alt="{{ section.settings.img_1.alt }}"class="pc background-image">{% endif %}{% if section.settings.img_mobile_1 != blank %}<imgsrc="{{ section.settings.img_mobile_1 | image_url: width:'750x' }}"alt="{{ section.settings.img_mobile_1.alt }}"loading="lazy"class="mobile background-image">{% endif %}</div><!-- 第二屏:白底大字 --><div class="videoContainer_middle"><h2 class="zoom-title page-width">{{ section.settings.title }}</h2> </div><div class="zoom-inner-bottom page-width"><div class="zoom-inner-bottom-inner">{% if section.settings.title-text  != blank  %}<div class="zoom-inner-bottom-title">{{ section.settings.title-text }}</div>{% endif %}{% if section.settings.text  != blank  %}<div class="zoom-inner-bottom-text text-text">{{ section.settings.text }}</div>{% endif %}{% if section.settings.svg  != blank  %}<div class="zoom-inner-bottom-svg">{{ section.settings.svg }}</div>{% endif %}{% if section.settings.btn_link  != blank and section.settings.btn_text != blank %}<a href="{{ section.settings.btn_link }}" class="zoom-inner-bottom-btn btn_2">{{ section.settings.btn_text }}</a>{% endif %}</div></div></div>
</div><script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
<script>document.addEventListener('DOMContentLoaded', function () {gsap.registerPlugin(ScrollTrigger);// 初始状态gsap.set('.videoContainer_top', { opacity: 1 });gsap.set('.video-wrapper', { width: 1920, height: 911 });gsap.set('.top-text', { y: -96, maxHeight: 0, overflow: 'hidden' });gsap.set('.bottom-text', { y: 206, maxHeight: 0, overflow: 'hidden' });// gsap.set('.videoContainer_middle', { opacity: 0, scale: 3.5 }); // 第二屏初始很大并隐藏gsap.set('.videoContainer_middle', { opacity: 0, scale: 7 });// 主时间轴let masterTl = gsap.timeline({ defaults: { ease: 'power2.inOut' } });// 第一屏保持显示masterTl.to('.videoContainer_top', { opacity: 1, ease: 'none' });// 第二屏出现masterTl.to('.videoContainer_middle', { opacity: 1, scale: 1, duration: 1.2,  ease: 'power2.out' });// 在上一个动画进行到 70% 时开始文字动画masterTl.to('.zoom-title', { y: '-30%',   duration: 1,  ease: 'power2.inOut' }, '-=0.7'); // 1.2 * 0.7 = 0.84,减掉0.36意味着动画在前一个动画进行70%左右时开始// 图片从上往下进入masterTl.fromTo('.zoom-inner-top', { yPercent: -100, xPercent: -50, opacity: 0 },   { yPercent: 0, xPercent: -50, opacity: 1, duration: 1, ease: 'power2.out' }, '<');// 底部文字从下往上进入masterTl.fromTo('.zoom-inner-bottom', { y: '100%', opacity: 0 },   { y: '0%', opacity: 1, duration: 1, ease: 'power2.out' }, '<' // 和上一个动画同时执行);// 第一屏上下文字展开masterTl.to('.top-text',{y: 0,maxHeight: 1000,onStart: () => {gsap.set('.top-text', { overflow: 'visible' });},},'<');masterTl.to('.bottom-text',{y: 0,maxHeight: 1000,onStart: () => {gsap.set('.bottom-text', { overflow: 'visible' });},},'<');// 计算 ScrollTrigger 结束位置function calculateEndOffset() {const totalDuration = masterTl.totalDuration();return totalDuration * 1000;}let scrollTrigger;function initScrollTrigger() {if (scrollTrigger) {scrollTrigger.kill();masterTl.progress(0).pause();}scrollTrigger = ScrollTrigger.create({trigger: '#nebula-scroll-section',start: 'top top',end: `+=${calculateEndOffset()}`,scrub: true,pin: true,animation: masterTl,invalidateOnRefresh: true,});}initScrollTrigger();// resize 防抖let resizeTimeout;window.addEventListener('resize', () => {clearTimeout(resizeTimeout);resizeTimeout = setTimeout(() => {initScrollTrigger();ScrollTrigger.refresh();}, 200);});// 动态内容变化监听const nebulaSection = document.getElementById('nebula-scroll-section');if (nebulaSection) {new ResizeObserver(() => {initScrollTrigger();ScrollTrigger.refresh();}).observe(nebulaSection);}});
</script>

http://www.dtcms.com/a/350301.html

相关文章:

  • 《零基础学 C 语言文件顺序读写:fputc/fgetc 到 fread/fwrite 函数详解》
  • 并行算法与向量化指令集的实战经验
  • 【Linux内核实时】实时互斥锁 - sched_rt_mutex
  • 寂静之歌 单机+联机(Songs Of Silence)免安装中文版
  • 数据存储的思考——从RocketMQ和Mysql的架构入手
  • 力扣498 对角线遍历
  • Qwen2-Plus与DeepSeek-V3深度测评:从API成本到场景适配的全面解析
  • 消费场景的构建来自哪些方面?
  • KEPServerEX——工业数据采集与通信的标准化平台
  • 处理端口和 IP 地址
  • 最新刀客IP地址信息查询系统源码_含API接口_首发
  • AI被干冒烟了
  • HTML+CSS+JavaScript实现的AES加密工具网页应用,包含完整的UI界面和加密/解密功能
  • 系统开发 Day4
  • idea官网选择具体版本的下载步骤
  • 解决VSCode终端中文乱码问题
  • Cursor入门
  • Node.js面试题及详细答案120题(43-55) -- 性能优化与内存管理篇
  • HarmonyOS 中的 @Prop:深入理解单向数据传递机制
  • Java多态大冒险:当动物们开始“造反”
  • K8s高可用:Master与候选节点核心解析
  • STM32高级定时器-输出比较模式
  • 基于周期因子的资金流入流出预测
  • 区间和使用前缀和方法得到的时间复杂度
  • 2025 高教社杯全国大学生数学建模竞赛A题B题C题D题E题思路+模型+代码+论文(9.4开赛后第一时间更新)
  • AD画PCB时不小心移除的焊盘如何恢复
  • 玩转ChatGPT:Kimi深度研究功能
  • 模拟IC设计基础系列10-virtuoso常用快捷键整理(基础操作)
  • 驱动清理工具Driver Store Explorer(驱动程序资源管理器) 中文便携版
  • 重学前端010 --- 响应式网页设计 中级CSS