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

如何在容器离开视口顶部时停止视差滚动

问题描述

我制作了一个包含四张图片的山地视差场景,当容器在屏幕底部进入视口时,图片会产生视差滚动动画。但当容器开始从视口顶部移出时,视差效果出现反向。以下是当前的视差脚本,请问如何在容器div离开视口时停止视差滚动?

/**
   * Creates an IntersectionObserver and starts observing all elements found using the selector.
   *
   * @param {String} selector: Selector used to find all target elements
   * @param {Number[]} threshold: Array of intersection ratios, at which the callback is executed
   * @param {Function} callback: Callback executed for each threshold
   */
  function observe(selector, threshold, callback) {
    const elements = document.querySelectorAll(selector);
    const options = {
      rootMargin: '-100px',
      threshold: threshold,
    };

    const observer = new IntersectionObserver(callback, options);

    for (const element of elements) {
      observer.observe(element);
    }
  }

  /**
   * Creates a CSS translateY value.
   *
   * @param {Number} ratio: A number between 0 and 1
   * @param {String} total: A valid CSS number and unit (10px, 100%, 30vh, …)
   * @return {String} The CSS translateY value.
   */
  function translateY(ratio, total) {
    return `translateY(calc(-${ratio} * ${total})`;
  }

  /**
   * Callback executed for the box elements
   *
   * @see https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
   *
   * @param {IntersectionObserverEntry[]} entries: Intersection Observer Entries
   * @param {IntersectionObserver} observer: Intersection Observer
   */
  function boxParallax(entries, observer) {
    for (const entry of entries) {
      if (entry.isIntersecting) {
        entry.target.style.transform = translateY(entry.intersectionRatio, '-140px');
      }
    }
  }

  function boxParallax_two(entries, observer) {
    for (const entry of entries) {
      if (entry.isIntersecting) {
        entry.target.style.transform = translateY(entry.intersectionRatio, '-270px');
      }
    }
  }

  function boxParallax_three(entries, observer) {
    for (const entry of entries) {
      if (entry.isIntersecting) {
        entry.target.style.transform = translateY(entry.intersectionRatio, '-300px');
      }
    }
  }

  function boxParallax_four(entries, observer) {
    for (const entry of entries) {
      if (entry.isIntersecting) {
        entry.target.style.transform = translateY(entry.intersectionRatio, '-350px');
      }
    }
  }
  /**
   * Create one threshold for each intersection ratio.
   *
   * @return {Number[]}
   */
  function createThreshold() {
    const threshold = [];
    for (let i = 0; i <= 1.0; i += 0.01) {
      threshold.push(i);
    }

    return threshold;
  }

  const threshold = createThreshold();
  observe('.two', threshold, boxParallax);
  observe('.three', threshold, boxParallax_two);
  observe('.four', threshold, boxParallax_three);
  observe('.five', threshold, boxParallax_four);

});
解决方案

问题核心是:元素从视口顶部移出时,intersectionRatio会从1逐步降到0,但此时isIntersecting仍为true,导致视差动画反向。我们需要固定住元素完全进入视口后的最大位移,不再随比例下降而变化。

1. 修复语法错误

首先补全translateY函数的闭合括号,否则CSS样式无效:

function translateY(ratio, total) {
  return `translateY(calc(-${ratio} * ${total}))`;
}

2. 统一并优化回调逻辑

为每个元素记录最大的intersectionRatio,仅在比例上升时更新位移,比例下降时保持最大位移:

// 生成统一的视差回调,接收总位移参数
function boxParallaxHandler(totalOffset) {
  return function(entries, observer) {
    for (const entry of entries) {
      // 初始化元素的最大比例记录
      if (!entry.target._maxIntersectRatio) {
        entry.target._maxIntersectRatio = 0;
      }

      if (entry.isIntersecting) {
        // 仅当当前比例大于记录的最大值时,更新位移
        if (entry.intersectionRatio > entry.target._maxIntersectRatio) {
          entry.target._maxIntersectRatio = entry.intersectionRatio;
          entry.target.style.transform = translateY(entry.target._maxIntersectRatio, totalOffset);
        }
      } else {
        // 元素完全离开视口时,重置最大比例(可选)
        entry.target._maxIntersectRatio = 0;
        // 可选:重置元素位置
        // entry.target.style.transform = 'translateY(0)';
      }
    }
  };
}

3. 替换原有的多回调调用

用统一的回调处理函数替换原来的四个独立回调:

const threshold = createThreshold();
observe('.two', threshold, boxParallaxHandler('-140px'));
observe('.three', threshold, boxParallaxHandler('-270px'));
observe('.four', threshold, boxParallaxHandler('-300px'));
observe('.five', threshold, boxParallaxHandler('-350px'));

可选调整:修改rootMargin

如果希望元素完全进入视口后就停止动画,可以将observe函数中的rootMargin设为0px,确保元素完全可见后不再触发回调更新。

这样修改后,容器从底部进入视口时正常执行视差动画,从顶部移出时保持最大位移不再反向,完全离开视口后可选择重置状态。

内容的提问来源于stack exchange,提问作者Breon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 02:00:38