视差动画在图片滚动至可视区域时延迟的修复咨询
修复横向滚动视差效果的延迟问题
你提到的视差延迟问题,主要是因为当前代码的滚动事件处理逻辑存在防抖延迟,以及视差更新的触发机制不够直接导致的。下面是具体的修复方案和优化后的代码:
核心问题分析
- 滚动防抖延迟:原代码中设置的100ms防抖延迟,导致滚动过程中视差更新跟不上滚动速度
- 状态标志位冗余:
isScrolling和isScrollingStopped两个标志位增加了逻辑复杂度,容易引发更新滞后 - 静态窗口宽度:初始化时仅获取一次窗口宽度,窗口resize后逻辑无法自动适配
- 性能优化不足:滚动事件监听未设置
passive: true,可能影响滚动流畅度
修复方案
- 移除滚动防抖延迟逻辑,直接在滚动事件中通过
requestAnimationFrame触发视差更新,确保每帧都能响应滚动 - 监听窗口
resize事件,实时更新窗口宽度,保证响应式场景下逻辑正确 - 简化状态管理,去掉冗余标志位,直接通过
requestAnimationFrame循环控制视差更新 - 给滚动事件添加
passive: true,提升滚动性能 - 调整Intersection Observer阈值,让灰度切换更自然
优化后的完整代码
JavaScript
document.addEventListener("DOMContentLoaded", function() { const parallaxContainer = document.querySelector(".parallax-container"); let windowWidth = window.innerWidth; let animationId = null; // 存储requestAnimationFrame的ID // 监听窗口resize,实时更新窗口宽度 window.addEventListener("resize", () => { windowWidth = window.innerWidth; }); // Intersection Observer配置 const observerOptions = { root: null, rootMargin: "0px", threshold: 0.5 // 图片一半进入视口时触发状态切换 }; const observer = new IntersectionObserver(function(entries) { entries.forEach((entry) => { const image = entry.target; if (windowWidth < 670) { image.style.filter = entry.isIntersecting ? "grayscale(0)" : "grayscale(1)"; } }); }, observerOptions); const parallaxImages = document.querySelectorAll(".parallax-image"); parallaxImages.forEach((item) => { observer.observe(item); }); // 视差更新函数 function updateParallax() { const scrollPosition = parallaxContainer.scrollLeft; parallaxImages.forEach((item) => { const coefficient = parseFloat(item.dataset.parallaxCoefficient) || 0.2; const xOffset = -scrollPosition * coefficient; item.style.backgroundPositionX = `${xOffset}px`; }); animationId = requestAnimationFrame(updateParallax); } // 滚动事件监听,使用passive提升性能 parallaxContainer.addEventListener("scroll", () => { if (!animationId) { animationId = requestAnimationFrame(updateParallax); } }, { passive: true }); // 滚动停止时取消动画帧 parallaxContainer.addEventListener("scrollend", () => { if (animationId) { cancelAnimationFrame(animationId); animationId = null; } }); });
CSS
.what-we-do { padding: 88px 60px 40px 60px; } .what-we-do .upper-div h2 { margin: 0; } .what-we-do h2 { font-family: "lato"; font-style: normal; font-weight: 300; font-size: 40px; line-height: 48px; } .what-we-do-container { height: 100%; display: flex; flex-direction: column; } .pics-div { display: flex; flex-direction: row; gap: 8px; height: 576px; overflow: hidden; } .pics-div p { font-family: Lato; font-size: 20px; font-weight: 400; line-height: 28px; letter-spacing: 0em; text-align: left; } .pic1, .pic2, .pic3 { cursor: pointer; filter: grayscale(1); background-size: cover; width: 30%; padding: 32px; display: flex; flex-direction: column; justify-content: space-between; color: white; transition-duration: 0.5s; } .pic1 { background-image: url(https://picsum.photos/200/300); } .pic2 { background-image: url(https://picsum.photos/200/300); } .pic3 { background-image: url(https://picsum.photos/200/300); } .pic-upper-text { display: flex; gap: 32px; align-items: center; transition-duration: 0.5s; position: relative; } .pic1:hover, .pic2:hover, .pic3:hover { width: 50%; filter: grayscale(0); .pic-upper-text { gap: 150px; } } .parallax-container { display: flex; overflow-x: scroll; scroll-behavior: auto; -webkit-overflow-scrolling: touch; /* 恢复移动端流畅滚动 */ scroll-snap-type: x mandatory; } .parallax-image { scroll-snap-align: center; flex: auto; }
HTML
<section class="what-we-do"> <div class="what-we-do-container"> <div class="upper-div"> <h2>Main title</h2> </div> <div class="pics-div parallax-container"> <div ontouchstart="changeBullet1()" onclick="window.location.href = 'lorem-ipsum';" class="pic1 parallax-image" data-parallax-coefficient="0.2"> <div class="pic-upper-text"> <h3>Title1</h3> <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M8.33398 20H31.6673" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M20 8.33325L31.6667 19.9999L20 31.6666" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </svg> </div> <p>Lorem ipsum</p> </div> <div ontouchstart="changeBullet2()" onclick="window.location.href = 'lorem-ipsum';" class="pic2 parallax-image" data-parallax-coefficient="0.2"> <div class="pic-upper-text"> <h3>Title 2</h3> <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M8.33398 20H31.6673" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M20 8.33325L31.6667 19.9999L20 31.6666" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </svg> </div> <p>Lorem ipsum sit dolor amet</p> </div> <div ontouchstart="changeBullet3()" onclick="window.location.href = 'lorem-ipsum';" class="pic3 parallax-image" data-parallax-coefficient="0.2"> <div class="pic-upper-text"> <h3>Title 3</h3> <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M8.33398 20H31.6673" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M20 8.33325L31.6667 19.9999L20 31.6666" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </svg> </div> <p>Lorem ipsum sit dolor amet</p> </div> </div> <div class="bullet-container"> <div class="bullet-box"> <div id="bullet1" class="bullet"></div> <div id="bullet2" class="bullet"></div> <div id="bullet3" class="bullet"></div> </div> </div> </div> </section>
额外优化点
- 恢复移动端
-webkit-overflow-scrolling: touch属性,提升移动端滚动流畅度 - 统一CSS过渡时长,避免样式冲突
- 移除原代码中未定义的
animation相关无效逻辑 - 简化Intersection Observer的状态切换代码,提升可读性
内容的提问来源于stack exchange,提问作者iorgu
相关产品推荐
相关产品推荐

