如何用Vanilla JS复刻GSAP ScrollTrigger滚动驱动动画效果
复刻GSAP ScrollTrigger的滚动驱动动画效果
需求描述
现有一个基于requestAnimationFrame的循环函数,通过scroll事件监听scrollY值,控制图片从left: 0移动至left: 100%;图片的父元素设置了min-height: 100vh。
需要实现:
- 父元素高度持续扩展至超过100vh,直到图片的
left值达到100% - 动画完成后,父元素停止扩展,随图片一同向上滚动
- 不能使用
position: sticky,要让用户感觉下方DOM元素被「固定」,直到图片动画完成才进入视野
核心问题:如何设置父div的高度,使得滚动到该div底部时,子元素的动画恰好完成?
基础代码示例
<div class="parent"> <img src="image.jpg" alt=""> </div>
核心思路
要实现该效果,关键是计算出动画完成所需的滚动距离,然后将父元素高度设置为「视口高度 + 动画所需滚动距离」。这样用户从进入父元素到滚到底部的过程,刚好对应图片完成整个位移动画,之后父元素随页面正常滚动,下方内容进入视野。
具体实现步骤
计算动画核心参数
- 确定图片从
left:0到left:100%的总位移量:即父元素宽度减去图片自身宽度(避免图片超出父元素) - 设定滚动驱动速率:比如每滚动1px,图片移动0.3px(数值越小,完成动画需要的滚动距离越长,可按需调整)
- 确定图片从
动态设置父元素高度
- 根据总位移量和滚动速率,算出完成动画需要的滚动距离:
总位移量 / 滚动速率 - 父元素最终高度 =
100vh + 滚动距离
- 根据总位移量和滚动速率,算出完成动画需要的滚动距离:
滚动监听与动画更新
- 监听
scroll事件,计算当前滚动位置相对于父元素的动画进度(0-1区间) - 根据进度更新图片的
left值,确保动画仅在父元素的滚动范围内执行
- 监听
完整代码实现
<!DOCTYPE html> <html lang="zh-CN"> <head> <style> * { margin: 0; padding: 0; box-sizing: border-box; } .parent { min-height: 100vh; position: relative; overflow: hidden; background: #f0f0f0; } .parent img { position: absolute; top: 50%; left: 0; transform: translateY(-50%); width: 200px; height: auto; } .content-below { height: 100vh; background: #333; color: #fff; display: flex; justify-content: center; align-items: center; font-size: 2rem; } </style> </head> <body> <div class="parent"> <img src="https://picsum.photos/200/300" alt="示例图片"> </div> <div class="content-below">下方内容,动画完成后才会显示</div> <script> const parent = document.querySelector('.parent'); const img = parent.querySelector('img'); // 动画参数配置 const scrollRate = 0.3; // 滚动驱动速率,按需调整 const parentWidth = parent.offsetWidth; const totalAnimationDistance = parentWidth - img.offsetWidth; const requiredScrollDistance = totalAnimationDistance / scrollRate; // 设置父元素高度 parent.style.height = `${window.innerHeight + requiredScrollDistance}px`; // 更新动画进度 function updateAnimation() { const parentTop = parent.getBoundingClientRect().top + window.scrollY; // 计算滚动进度,限制在0-1区间 const scrollProgress = Math.min(1, Math.max(0, (window.scrollY - parentTop) / requiredScrollDistance)); img.style.left = `${scrollProgress * totalAnimationDistance}px`; } // 滚动监听优化 let ticking = false; window.addEventListener('scroll', () => { if (!ticking) { window.requestAnimationFrame(() => { updateAnimation(); ticking = false; }); ticking = true; } }); // 窗口 resize 适配 window.addEventListener('resize', () => { const newParentWidth = parent.offsetWidth; const newTotalDistance = newParentWidth - img.offsetWidth; const newScrollDistance = newTotalDistance / scrollRate; parent.style.height = `${window.innerHeight + newScrollDistance}px`; updateAnimation(); }); </script> </body> </html>
关键细节说明
- 滚动进度控制:通过
scrollY和父元素的offsetTop计算动画进度,确保进度始终在0-1之间,避免图片超出父元素范围 - 窗口适配:监听窗口resize事件,重新计算动画参数,保证窗口大小改变时动画效果依然正确
- 性能优化:用
requestAnimationFrame包裹滚动回调,避免频繁触发导致页面卡顿
内容的提问来源于stack exchange,提问作者Mr.Coder
相关产品推荐
相关产品推荐

