滚动动画适配不同页面高度:如何按视口范围精准触发?
问题场景
滚动动画在测试页面可正常运行,但在最终页面中因页面高度不同,动画触发时机远早于预期。需求是让动画在目标元素从视口100%可见滚动到0%可见的过程中触发,适配任意页面高度。
原代码如下:
原JS代码
var scroll = document.querySelector ('.curve'); window.addEventListener ('scroll', function(){ var value = 1 + window.scrollY / -200; scroll.style.transform = `scaleY(${value})`; })
原CSS代码
* { margin:0; padding:0; } body { height:200vh; background-color:#111; } section { position:absolute; width:100%; height:100%; background:#ff2; } section .curve { position:absolute; bottom:-200px; height:200px; width:100%; transform-origin:top; } section .curve img{ width:100%; height:100%; }
原HTML代码
<body> <section> <span class="curve"> <img src="https://wordpress-899937-3173615.cloudwaysapps.com/wp-content/uploads/2023/01/curve-white-300x59.png"> </span> </section> </body>
解决方案
核心思路是基于元素在视口中的可见比例计算动画参数,而非依赖固定的滚动距离数值,这样就能适配任意页面高度。
修改后的JS代码如下:
const curveEl = document.querySelector('.curve'); const curveHeight = curveEl.offsetHeight; window.addEventListener('scroll', () => { // 获取元素相对于视口的位置信息 const rect = curveEl.getBoundingClientRect(); // 计算元素在视口中的可见高度:顶部在视口内取顶部到底部,否则取视口底部到元素顶部的距离,最小为0 const visibleHeight = Math.max(0, Math.min(curveHeight, window.innerHeight - rect.top)); // 计算可见比例,范围0-1 const visibleRatio = visibleHeight / curveHeight; // 将比例作为scaleY的值,实现从1到0的平滑过渡 curveEl.style.transform = `scaleY(${visibleRatio})`; });
代码说明
- 预取元素高度:提前获取
.curve元素的高度,避免每次滚动都重新计算,优化性能 - 实时计算可见区域:利用
getBoundingClientRect()获取元素在视口中的实时位置,计算当前可见高度 - 推导可见比例:用可见高度除以元素总高度,得到0-1之间的比例值
- 应用动画变换:将比例值作为
scaleY参数,元素完全在视口中时比例为1(无缩放),完全滚出时比例为0(完全收缩),精准匹配需求的触发时机
性能优化补充
可添加requestAnimationFrame减少滚动事件的触发频率,提升流畅度:
const curveEl = document.querySelector('.curve'); const curveHeight = curveEl.offsetHeight; let ticking = false; function updateAnimation() { const rect = curveEl.getBoundingClientRect(); const visibleHeight = Math.max(0, Math.min(curveHeight, window.innerHeight - rect.top)); const visibleRatio = visibleHeight / curveHeight; curveEl.style.transform = `scaleY(${visibleRatio})`; ticking = false; } window.addEventListener('scroll', () => { if (!ticking) { requestAnimationFrame(updateAnimation); ticking = true; } });
内容的提问来源于stack exchange,提问作者Lisandro Cejas
相关产品推荐
相关产品推荐

