如何实现非首屏元素animation仅在进入可视区域时触发而非滚动即执行
解决方案
实现元素仅进入可视区域后触发动画,最推荐使用性能最优的Intersection Observer API,不需要监听高频滚动事件,也不会出现滚动就触发动画的问题,具体实现步骤如下:
方案1:Intersection Observer API(推荐)
CSS 配置
首先将动画逻辑和元素默认样式分离,默认不触发动画:
/* 目标元素默认样式 */ .scroll-animate { opacity: 0; transform: translateY(20px); transition: all 0.5s ease-out; /* 若使用@keyframes动画,默认关闭 animation: none; */ } /* 元素进入视口后添加的类,触发动画 */ .scroll-animate.active { opacity: 1; transform: translateY(0); /* 若使用@keyframes动画,打开配置 animation: 自定义动画名 0.5s ease-out forwards; */ }
JS 逻辑
// 获取所有需要触发动画的元素 const animateElements = document.querySelectorAll('.scroll-animate') // 初始化交叉观察者 const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { // 元素进入视口时触发 if (entry.isIntersecting) { entry.target.classList.add('active') // 仅需要触发一次动画时,触发后取消监听优化性能 observer.unobserve(entry.target) } }) }, { // 触发阈值:元素露出10%时触发,可自行调整为0~1的数值 threshold: 0.1, // 可选:提前触发的边距,例如设置'0px 0px 50px 0px'可提前50px触发动画 rootMargin: '0px' }) // 给所有目标元素绑定观察 animateElements.forEach(el => observer.observe(el))
方案2:滚动事件监听(仅用于兼容IE等旧浏览器)
如果需要兼容不支持Intersection Observer的旧浏览器,可使用滚动监听方案,需加防抖优化性能:
const animateElements = document.querySelectorAll('.scroll-animate') // 判断元素是否在视口内的工具函数 function checkInViewport(el) { const rect = el.getBoundingClientRect() return rect.top <= window.innerHeight && rect.bottom >= 0 } // 滚动处理函数加防抖,避免高频触发 let scrollTimer function handleScroll() { animateElements.forEach(el => { if (checkInViewport(el) && !el.classList.contains('active')) { el.classList.add('active') } }) } window.addEventListener('scroll', () => { clearTimeout(scrollTimer) scrollTimer = setTimeout(handleScroll, 100) }) // 页面加载后先执行一次,避免元素刚好在首屏边缘未触发 window.addEventListener('load', handleScroll)
注意事项
- 不要直接将
animation/transition的触发逻辑绑定在元素默认样式上,必须通过新增类的方式触发,否则页面加载完成就会自动执行动画 - 如果需要实现元素滚出视口再滚入时重新触发动画,删除
observer.unobserve(entry.target)代码,同时在元素滚出视口时移除active类即可
内容的提问来源于stack exchange,提问作者Farah Massuh
相关产品推荐
相关产品推荐

