基于滚动事件监听的CSS文本动画实现求助(仿viraly.com.br)
实现视口触发的文本滚动动画(LTR/RTL)
需求说明
需要实现两种文本滚动入场动画:
- LTR(从左到右):初始位置为
-200vh,元素进入视口后滚动到原位 - RTL(从右到左):初始位置为
164vh,元素进入视口后滚动到原位
滚动到元素进入视口范围时触发动画,现有Next.js代码效果未达预期,需修正实现逻辑。
现有代码问题分析
- DOM查询时机错误:在组件顶层直接调用
document.querySelectorAll,Next.js的SSR阶段会报错,且组件重渲染时会重复执行查询,未绑定到正确的组件生命周期。 - 滚动监听性能低下:使用
scroll事件监听,触发频率极高,易引发页面性能问题。 - 可见性判断过严:仅当元素完全进入视口才触发动画,不符合常规的用户体验逻辑(通常元素部分进入视口就应启动动画)。
- 无监听清理机制:滚动监听未在组件卸载时移除,会造成内存泄漏。
- CSS优先级不足:重置类的
transform属性优先级低于初始动画类,可能导致动画状态切换失效。
修正后的实现代码
Next.js 组件代码
'use client' // Next.js 13+ App Router 需添加此指令 import { useEffect, useRef } from 'react'; export function HartechAnimated() { const containerRef = useRef(null); useEffect(() => { // 配置交叉观察器参数 const observerOptions = { root: null, rootMargin: '0px', threshold: 0.1 // 元素10%进入视口时触发动画 }; // 交叉观察器回调逻辑 const observerCallback = (entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const target = entry.target; if (target.classList.contains('animateRTL')) { target.classList.add('resetXRTL'); } else if (target.classList.contains('animateLTR')) { target.classList.add('resetXLTR'); } // 仅触发一次动画,避免重复执行 observer.unobserve(target); } }); }; // 初始化交叉观察器 const observer = new IntersectionObserver(observerCallback, observerOptions); const animateElements = containerRef.current?.querySelectorAll('.animateRTL, .animateLTR'); // 为目标元素添加监听 if (animateElements) { animateElements.forEach(el => observer.observe(el)); } // 组件卸载时清理监听 return () => { if (animateElements) { animateElements.forEach(el => observer.unobserve(el)); } }; }, []); return ( <div ref={containerRef} className="font-britanica-black-expanded overflow-hidden text-6xl text-white py-20 md:text-black md:text-9xl md:py-36" > <b className="block animateLTR">HARTECH REPARO</b> <b className="block hartech-stroke animateRTL">DE PLACAS DE VÍDEO</b> <b className="block animateLTR">HARTECH REPARO</b> </div> ); }
CSS 代码
/* 初始动画状态 */ .animateRTL { transform: translateX(164vh); transition: transform 2.2s ease-in-out; } .animateLTR { transform: translateX(-200vh); transition: transform 2s ease-in-out; } /* 重置状态(通过组合选择器提高优先级) */ .animateRTL.resetXRTL, .animateLTR.resetXLTR { transform: translateX(0); }
关键改进说明
- 采用Intersection Observer:替代传统滚动监听,性能更优,可通过
threshold精准控制动画触发时机。 - 规范组件生命周期:在
useEffect中处理DOM操作与监听逻辑,确保仅在客户端执行,组件卸载时自动清理监听,避免内存泄漏。 - 优化CSS优先级:使用组合选择器提升重置类的优先级,确保动画状态切换生效。
- 单次动画触发:元素进入视口后停止监听,避免重复触发动画,符合预期交互逻辑。
内容的提问来源于stack exchange,提问作者Gustavo Diniz
相关产品推荐
相关产品推荐

