You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

视差动画在图片滚动至可视区域时延迟的修复咨询

修复横向滚动视差效果的延迟问题

你提到的视差延迟问题,主要是因为当前代码的滚动事件处理逻辑存在防抖延迟,以及视差更新的触发机制不够直接导致的。下面是具体的修复方案和优化后的代码:

核心问题分析

  • 滚动防抖延迟:原代码中设置的100ms防抖延迟,导致滚动过程中视差更新跟不上滚动速度
  • 状态标志位冗余:isScrolling和isScrollingStopped两个标志位增加了逻辑复杂度,容易引发更新滞后
  • 静态窗口宽度:初始化时仅获取一次窗口宽度,窗口resize后逻辑无法自动适配
  • 性能优化不足:滚动事件监听未设置passive: true,可能影响滚动流畅度

修复方案

  1. 移除滚动防抖延迟逻辑,直接在滚动事件中通过requestAnimationFrame触发视差更新,确保每帧都能响应滚动
  2. 监听窗口resize事件,实时更新窗口宽度,保证响应式场景下逻辑正确
  3. 简化状态管理,去掉冗余标志位,直接通过requestAnimationFrame循环控制视差更新
  4. 给滚动事件添加passive: true,提升滚动性能
  5. 调整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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 18:45:54