环形滚动动画卡顿问题排查求助
环形滚动动画卡顿问题排查求助
各位前端大佬好,我是刚入门的前端新手,最近在做个人作品集的技能展示模块,想实现一个技能标签无限循环滚动的动画效果。我用Claude 3.7生成了相关的CSS和React代码,但实际运行起来动画特别卡顿,掉帧很明显,试了好多次调试都没解决,实在没办法来求助大家了😣
先简单说下场景:这个技能模块有滚动和网格两种视图模式,切换到滚动视图时,技能标签应该匀速无限循环滚动,但现在动画很不流畅,肉眼就能看出卡顿。
我已经尝试过的优化手段:
- 用
translate3d代替普通translate,触发GPU加速 - 添加
will-change: transform提示浏览器提前优化 - 采用
linear动画时间函数保证匀速滚动 - 确保动画只修改
transform属性,避免触发重排重绘
但这些都没解决卡顿问题,下面是我用到的相关代码,麻烦大家帮我看看哪里出了问题:
自定义滚动动画CSS
/* Enhanced scroll animation styles with forced continuous animation */ .scroll-wrapper { overflow: hidden; white-space: nowrap; width: 100%; position: relative; } .scroll-content, .scroll-content-reverse { display: inline-flex; white-space: nowrap; animation-timing-function: linear; animation-iteration-count: infinite; animation-play-state: running; will-change: transform; } .scroll-content { animation-name: continuous-scroll; } .scroll-content-reverse { animation-name: continuous-scroll; animation-direction: reverse; } @keyframes continuous-scroll { 0% { transform: translate3d(0%, 0, 0); } 100% { transform: translate3d(-50%, 0, 0); } }
React技能组件代码
'use client'; import { Skill as SkillType } from '@/types/skill'; import { skillTypeOrder } from '@/types/skill'; import { fetchSkills } from '@/services/skillService'; import { useTheme } from '@/context/ThemeContext'; import { Icon } from '@iconify/react'; import { useEffect, useState, useRef } from 'react'; import { motion } from 'framer-motion'; const Skill = () => { const { isDarkMode } = useTheme(); const [isVisible, setIsVisible] = useState(false); const [skillsData, setSkillsData] = useState<SkillType[]>([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState<string | null>(null); const [viewMode, setViewMode] = useState<'scroll' | 'grid'>('scroll'); const [isScrolling, setIsScrolling] = useState(false); // Fetch skills data from the database useEffect(() => { const getSkillsData = async () => { try { const data = await fetchSkills(); setSkillsData(data); setIsLoading(false); } catch (err) { console.error('Failed to fetch skills data:', err); setError('Failed to load skills data'); setIsLoading(false); } }; getSkillsData(); setIsVisible(true); }, []); const groupedSkills = skillsData.reduce((acc, skill) => { if (!acc[skill.type]) { acc[skill.type] = []; } acc[skill.type].push(skill); return acc; }, {} as Record<string, SkillType[]>); // Animation variants for the grid items const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.1 } } }; const itemVariants = { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { duration: 0.5 } } }; // Toggle view mode const toggleViewMode = () => { setViewMode(prev => prev === 'scroll' ? 'grid' : 'scroll'); }; return ( <section id="skills" className="py-10 overflow-hidden mt-20"> <motion.h2 className="text-2xl md:text-3xl font-bold mb-6 text-center drop-shadow-xl" initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, amount: 0.5 }} transition={{ duration: 0.5 }} > Skills </motion.h2> {/* View mode toggle */} <motion.div className="flex justify-center mb-8" initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, amount: 0.5 }} transition={{ duration: 0.5, delay: 0.3 }} > <div className="inline-flex items-center bg-[var(--card-bg)] rounded-full p-1 border border-[var(--border-color)] shadow-md"> <button onClick={() => setViewMode('scroll')} className={`px-4 py-2 rounded-full text-sm font-medium transition-all duration-300 ${ viewMode === 'scroll' ? 'bg-[var(--button-bg)] text-white shadow-sm' : 'text-[var(--card-text)] hover:bg-[var(--hover-bg)]' }`} > <div className="flex items-center space-x-1"> <Icon icon="mdi:animation-outline" className="w-4 h-4" /> <span>Scroll</span> </div> </button> <button onClick={() => setViewMode('grid')} className={`px-4 py-2 rounded-full text-sm font-medium transition-all duration-300 ${ viewMode === 'grid' ? 'bg-[var(--button-bg)] text-white shadow-sm' : 'text-[var(--card-text)] hover:bg-[var(--hover-bg)]' }`} > <div className="flex items-center space-x-1"> <Icon icon="mdi:grid" className="w-4 h-4" /> <span>Grid</span> </div> </button> </div> </motion.div> {isLoading ? ( <div className="flex justify-center items-center py-20"> <Icon icon="mdi:loading" className="animate-spin text-4xl text-blue-500" /> </div> ) : error ? ( <div className="text-center py-10 text-red-500"> <Icon icon="mdi:alert-circle" className="text-4xl mx-auto mb-2" /> <p>{error}</p> </div> ) : skillsData.length === 0 ? ( <div className="text-center py-10 text-[var(--text-secondary)]"> <p>No skills found</p> </div> ) : ( // 滚动视图核心渲染逻辑 <div className="scroll-wrapper"> <div className="scroll-content"> {skillsData.map(skill => ( <div key={skill.id} className="mx-4 flex items-center justify-center w-16 h-16 rounded-full bg-[var(--card-bg)] shadow-md p-2"> <Icon icon={skill.icon} className="text-2xl" /> </div> ))} {/* 复制一份实现无缝循环 */} {skillsData.map(skill => ( <div key={`${skill.id}-clone`} className="mx-4 flex items-center justify-center w-16 h-16 rounded-full bg-[var(--card-bg)] shadow-md p-2"> <Icon icon={skill.icon} className="text-2xl" /> </div> ))} </div> </div> )} </section> ); };
补充几个我怀疑的点:
- 组件里同时使用了framer-motion的入场动画和自定义CSS滚动动画,会不会两者有性能冲突?
- 技能项使用了Iconify的Icon组件,会不会是SVG图标的渲染开销导致动画掉帧?
- 我用复制技能项的方式实现无缝循环,这种方式会不会带来额外的性能负担?
希望各位大佬能帮我分析下问题所在,或者给一些优化建议,谢谢大家了🙏
内容来源于stack exchange
相关产品推荐
相关产品推荐

