如何实现带全屏背景动画的Swiper.js现代滑块?解决GSAP缩放异常
修复Swiper+GSAP背景图缩放动画异常问题
需求与问题
需要实现一款现代滑块,要求每视图显示3个slide,搭配全屏背景图,滑块切换时背景图随索引同步切换。已用Swiper.js构建滑块,尝试用GSAP实现背景图从scale(0)到scale(1)的缩放动画,但仅autoAlpha属性正常工作,缩放动画出现异常。
问题根源
- CSS中
.prjctbg.activeBG的transition属性与GSAP动画逻辑冲突,导致缩放效果紊乱 - 非活跃背景图未设置初始动画状态,导致动画起始帧不一致
- Swiper配置的
slidesPerView值不符合需求,且loop模式下未处理动画叠加问题
修复后的代码
HTML
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.4.2/css/swiper.css" /> <!-- slider section --> <div class="projectSlider"> <img src="https://i.im.ge/2022/12/16/dHOzYJ.windows-FV6Pma5U-98-unsplash.jpg" data-index="0" alt="" class="prjctbg" /> <img src="https://i.im.ge/2022/12/16/dH2dSz.jenny-ueberberg-BaSeK7rwc1A-unsplash.jpg" data-index="1" alt="" class="prjctbg" /> <img src="https://i.im.ge/2022/12/16/dHOzYJ.windows-FV6Pma5U-98-unsplash.jpg" data-index="2" alt="" class="prjctbg" /> <img src="https://i.im.ge/2022/12/16/dH2dSz.jenny-ueberberg-BaSeK7rwc1A-unsplash.jpg" data-index="3" alt="" class="prjctbg" /> <img src="https://i.im.ge/2022/12/16/dHOzYJ.windows-FV6Pma5U-98-unsplash.jpg" data-index="4" alt="" class="prjctbg" /> <img src="https://i.im.ge/2022/12/16/dH2dSz.jenny-ueberberg-BaSeK7rwc1A-unsplash.jpg" data-index="5" alt="" class="prjctbg" /> <div class="swiper-container"> <div class="swiper-wrapper"> <div class="swiper-slide"> <div class="swiper-slide-container"> <a href="#" class="details"> <h1 class="elmt"> Audiovisual <br /> Design </h1> <p class="elmt"> <span> Video Installation</span> </p> </a> </div> </div> <div class="swiper-slide"> <div class="swiper-slide-container"> <a href="#" class="details"> <h1 class="elmt"> Exhibition <br /> Design </h1> <p class="elmt"> <span> Multimedia Exhibition</span> </p> </a> </div> </div> <div class="swiper-slide"> <div class="swiper-slide-container"> <a href="#" class="details"> <h1 class="elmt"> Texts and <br /> Images </h1> <p class="elmt"> <span> website + Print Magazine </span> </p> </a> </div> </div> <div class="swiper-slide"> <div class="swiper-slide-container"> <a href="#" class="details"> <h1 class="elmt"> Material and <br /> Light </h1> <p class="elmt"> <span> Short Video</span> </p> </a> </div> </div> <div class="swiper-slide"> <div class="swiper-slide-container"> <a href="./projectDetails.html" class="details"> <h1 class="elmt"> Dimension <br /> Design </h1> <p class="elmt"> <span> Rendered Image </span> </p> </a> </div> </div> <div class="swiper-slide"> <div class="swiper-slide-container"> <a href="./projectDetails.html" class="details"> <h1 class="elmt"> App <br /> Design </h1> <p class="elmt"> <span> Sustainability App</span> </p> </a> </div> </div> </div> <!-- Add Pagination --> <div class="swiper-pagination"></div> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.4.2/js/swiper.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.3/gsap.min.js"></script>
CSS
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;1,200&display=swap'); * { margin: 0; padding: 0; font-family: 'Poppins', sans-serif; box-sizing: border-box; } body a { text-decoration: none; color: #ffffff; } .projectSlider { width: 100vw; height: 100vh; position: relative; } .projectSlider .swiper-pagination-fraction, .projectSlider .swiper-pagination-custom, .projectSlider .swiper-container-horizontal > .swiper-pagination-bullets { bottom: 26px; } .projectSlider .swiper-slide.swiper-slide-prev { display: flex; justify-content: flex-end; } .projectSlider .swiper-slide.swiper-slide-next { display: flex; justify-content: flex-start; } .projectSlider .swiper-button-prev, .projectSlider .swiper-container-rtl .swiper-button-next { background-image: url("../images/icons/prev.svg"); left: inherit; right: 100px; } .projectSlider .swiper-button-next, .projectSlider .swiper-container-rtl .swiper-button-prev { background-image: url("../images/icons/next.svg"); right: 35px; left: auto; } .projectSlider .swiper-button-prev, .projectSlider .swiper-button-next { top: 94%; width: 50px; height: 50px; background-size: contain; background-position: center; } .projectSlider .container { width: 100%; height: 100%; } .projectSlider .prjctbg { position: absolute; width: 100%; height: 100%; object-fit: cover; filter: blur(8px); -webkit-filter: blur(8px); opacity: 0; transform: scale(0); z-index: 0; } .projectSlider .prjctbg.activeBG { z-index: 1; } .swiper-container { position: relative; width: 100%; height: 100%; } .swiper-slide { height: 100%; display: flex; align-items: center; justify-content: center; transition: all 200ms cubic-bezier(0.165, 0.84, 0.44, 1); transform: scale(0.8); text-align: center; } .swiper-slide p { overflow: hidden; color: #fff; font-size: 1em; } .swiper-slide p span { transform: translateY(100px); transition: transform 1000ms cubic-bezier(0.165, 0.84, 0.44, 1) 0.2s; display: block; } .swiper-slide h1 { color: #fff; font-size: 3.8em; line-height: 100%; transition: transform 1000ms cubic-bezier(0.165, 0.84, 0.44, 1) 0.2s; margin: 0px 0px 30px 0px; transform: scale(0.5); font-weight: 600; } .swiper-slide.swiper-slide-active { transform: scale(1); } .swiper-slide.swiper-slide-active p { opacity: 1; transition-delay: 0.5s; } .swiper-slide.swiper-slide-active p span { transform: translateY(0px); } .swiper-slide.swiper-slide-active h1 { transition-delay: 0.15s; transform: scale(1); }
JavaScript
const imgs = document.querySelectorAll(".prjctbg"); // 初始化第一个背景图为活跃状态 gsap.set(imgs[0], { autoAlpha: 1, scale: 1 }); imgs[0].classList.add("activeBG"); var swiper = new Swiper(".swiper-container", { pagination: { el: ".swiper-pagination", clickable: true, }, spaceBetween: 1, slidesPerView: 3, // 修改为每视图显示3个slide centeredSlides: true, roundLengths: true, loop: true, loopAdditionalSlides: 30, navigation: { nextEl: ".swiper-button-next", prevEl: ".swiper-button-prev", }, on: { slideChangeTransitionStart: function () { const slideIndex = this.realIndex; // 清除所有背景图的GSAP动画,避免叠加 gsap.killTweensOf(".prjctbg"); imgs.forEach((img, idx) => { if (idx === slideIndex) { img.classList.add("activeBG"); // 执行新背景图的入场动画 gsap.fromTo( img, 1, { autoAlpha: 0, scale: 0 }, { autoAlpha: 1, scale: 1, ease: "power2.out" } ); } else { img.classList.remove("activeBG"); // 非活跃背景图隐藏 gsap.set(img, { autoAlpha: 0, scale: 0 }); } }); }, }, breakpoints: { 768: { slidesPerView: 1, }, }, });
关键修改说明
- 移除CSS冲突属性:删除
.prjctbg.activeBG中的transition,让GSAP完全控制动画逻辑 - 统一初始状态:给所有
.prjctbg设置opacity:0;transform:scale(0),确保动画起始一致 - 修正Swiper配置:将
slidesPerView改为3,满足每视图显示3个slide的需求 - 避免动画叠加:每次切换时调用
gsap.killTweensOf清除旧动画,防止多个动画冲突 - 优化动画逻辑:初始化时设置第一个背景图的状态,loop模式下用
realIndex确保索引匹配
内容的提问来源于stack exchange,提问作者NAYMUR
相关产品推荐
相关产品推荐

