Next.js+TailwindCSS滚动动画卡顿重置问题求助
问题分析与解决方案
问题根源
你遇到的卡顿、重置问题,核心原因是百分比位移无法实现无缝循环动画:当前动画从x:100%滚动到x:-100%,但元素总宽度与容器宽度不匹配,导致每次循环时动画跳回初始位置,视觉上表现为卡顿重置;同时重复列表的位移逻辑没有对齐,无法实现平滑衔接。
修复后的代码
"use client"; import { motion } from "framer-motion"; import { useEffect, useRef, useState } from "react"; import { FaAws, FaReact, FaPython } from "react-icons/fa"; import { SiNextdotjs, SiMongodb, SiDjango } from "react-icons/si"; import { DiHtml5, DiCss3, DiJavascript1 } from "react-icons/di"; import { RiTailwindCssFill } from "react-icons/ri"; const technologies = [ { icon: <DiHtml5 />, name: "HTML" }, { icon: <DiCss3 />, name: "CSS" }, { icon: <RiTailwindCssFill />, name: "TailwindCSS" }, { icon: <DiJavascript1 />, name: "JavaScript" }, { icon: <FaReact />, name: "React.js" }, { icon: <SiNextdotjs />, name: "Next.js" }, { icon: <FaPython />, name: "Python" }, { icon: <SiDjango />, name: "Django" }, { icon: <SiMongodb />, name: "MongoDB" }, { icon: <FaAws />, name: "AWS" }, ]; const Scrool = () => { const [singleListWidth, setSingleListWidth] = useState(0); const singleListRef = useRef(null); // 获取单个技术列表的实际渲染宽度 useEffect(() => { if (singleListRef.current) { setSingleListWidth(singleListRef.current.offsetWidth); } }, []); return ( <section className="py-12"> <div className="container mx-auto"> <h2 className="text-3xl font-bold text-center mb-8"> Technologies I Know </h2> <div className="relative overflow-hidden"> {/* 隐藏容器,用于计算单个列表的宽度 */} <div ref={singleListRef} className="flex gap-2 whitespace-nowrap absolute opacity-0 pointer-events-none" > {technologies.map((tech, index) => ( <div key={index} className="flex flex-col items-center p-2 w-32 h-32 flex-shrink-0" > <div className="text-4xl text-accent">{tech.icon}</div> <span className="text-lg text-center">{tech.name}</span> </div> ))} </div> <motion.div className="flex gap-2 whitespace-nowrap will-change-transform" initial={{ x: 0 }} animate={{ x: `-${singleListWidth}px` }} transition={{ duration: 20, ease: "linear", repeat: Infinity, repeatType: "loop" }} > {/* 重复两次列表实现无缝衔接 */} {technologies.map((tech, index) => ( <div key={index} className="flex flex-col items-center p-2 w-32 h-32 flex-shrink-0" > <div className="text-4xl text-accent">{tech.icon}</div> <span className="text-lg text-center">{tech.name}</span> </div> ))} {technologies.map((tech, index) => ( <div key={index + technologies.length} className="flex flex-col items-center p-2 w-32 h-32 flex-shrink-0" > <div className="text-4xl text-accent">{tech.icon}</div> <span className="text-lg text-center">{tech.name}</span> </div> ))} </motion.div> </div> </div> </section> ); }; export default Scrool;
关键修改说明
- 动态获取列表宽度:添加隐藏容器计算单个技术列表的实际宽度,避免手动计算的误差,确保滚动距离完全匹配列表长度
- 无缝位移逻辑:将动画从
x:0滚动到-${singleListWidth}px,滚动完第一个列表后,第二个列表刚好衔接,实现无断层循环 - 性能优化:添加
will-change-transform提示浏览器提前优化动画性能,减少卡顿概率 - 明确循环类型:设置
repeatType: "loop"确保动画始终正向循环,不会出现反向滚动的异常
内容的提问来源于stack exchange,提问作者Bilgekaan Berberoglu
相关产品推荐
相关产品推荐

