如何在容器离开视口顶部时停止视差滚动
问题描述
我制作了一个包含四张图片的山地视差场景,当容器在屏幕底部进入视口时,图片会产生视差滚动动画。但当容器开始从视口顶部移出时,视差效果出现反向。以下是当前的视差脚本,请问如何在容器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
相关产品推荐
相关产品推荐

