使用Framer Motion实现的自定义动画光标莫名消失问题求助
自定义Framer Motion光标在Next.js中几分钟后消失的解决方案
我用Next.js + TailwindCSS + Framer Motion搭建作品集网站,基于Framer Motion实现了自定义动画光标,初始运行正常,但页面重载几分钟后光标会莫名消失。降级Framer Motion至v5.3.2可解决该问题,但该版本与React 18存在兼容Bug,无法使用,且无法稳定复现此Bug。
相关代码与配置
自定义光标组件代码
<motion.div variants={variants} animate={cursorContext?.cursorVariant} className={`h-5 w-5 rounded-full fixed top-[50vh] left-[50vw] pointer-events-none z-[999] mix-blend-difference ${showCursor ? 'block' : 'hidden'}`} />
动画变体配置
const variants = { regular: { x: cursorContext!.mousePosition.x - 5, y: cursorContext!.mousePosition.y - 5, border: "white 2px solid", }, clickable: { x: cursorContext!.mousePosition.x - 5, y: cursorContext!.mousePosition.y - 5, border: 'none', backgroundColor: 'white', width: 10, height: 10, }, largeClickable: { x: cursorContext!.mousePosition.x - 10, y: cursorContext!.mousePosition.y - 10, border: 'none', backgroundColor: 'white', width: 20, height: 20, }, };
项目依赖配置
{ "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start" }, "dependencies": { "next": "latest", "react": "18.2.0", "react-dom": "18.2.0" }, "devDependencies": { "@types/node": "18.11.3", "@types/react": "18.0.21", "@types/react-dom": "18.0.6", "@types/react-scroll": "^1.8.5", "autoprefixer": "^10.4.12", "framer-motion": "^7.6.15", "postcss": "^8.4.18", "react-icons": "^4.6.0", "react-scroll": "^1.8.8", "tailwindcss": "^3.2.1", "typescript": "4.8.4" } }
可能的解决方案
1. 拆分变体中的状态引用与样式
当前变体直接从cursorContext读取mousePosition,可能导致动画实例在状态更新时出现引用失效或内存泄漏。建议将位置计算移到组件内,变体只保留纯样式:
import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; // 组件内计算位置 const [position, setPosition] = useState({ x: 0, y: 0 }); useEffect(() => { if (!cursorContext) return; const offset = cursorContext.cursorVariant === 'largeClickable' ? 10 : 5; setPosition({ x: cursorContext.mousePosition.x - offset, y: cursorContext.mousePosition.y - offset, }); }, [cursorContext]); // 纯样式变体 const variants = { regular: { border: "white 2px solid" }, clickable: { border: 'none', backgroundColor: 'white', width: 10, height: 10 }, largeClickable: { border: 'none', backgroundColor: 'white', width: 20, height: 20 }, }; // 组件渲染 <motion.div animate={{ ...position, ...variants[cursorContext?.cursorVariant || 'regular'] }} className={`h-5 w-5 rounded-full fixed top-[50vh] left-[50vw] pointer-events-none z-[999] mix-blend-difference ${showCursor ? 'block' : 'hidden'}`} />
2. 升级Framer Motion到最新稳定版
v7.6.15存在React 18下的潜在兼容问题,后续版本可能已修复动画实例回收的Bug,执行命令升级:
npm install framer-motion@latest
3. 排查showCursor状态异常
检查控制showCursor的逻辑是否存在误触发的情况,添加日志追踪状态变化:
useEffect(() => { console.log('showCursor状态变更:', showCursor); }, [showCursor]);
4. 手动控制动画实例
使用useAnimation钩子手动管理动画,避免自动动画的潜在冲突:
import { useAnimation } from 'framer-motion'; const controls = useAnimation(); useEffect(() => { if (!cursorContext) return; const variant = variants[cursorContext.cursorVariant || 'regular']; const offset = cursorContext.cursorVariant === 'largeClickable' ? 10 : 5; controls.start({ x: cursorContext.mousePosition.x - offset, y: cursorContext.mousePosition.y - offset, ...variant, }); }, [cursorContext, controls]); <motion.div animate={controls} className={`h-5 w-5 rounded-full fixed top-[50vh] left-[50vw] pointer-events-none z-[999] mix-blend-difference ${showCursor ? 'block' : 'hidden'}`} />
5. 临时关闭React Strict Mode测试
React 18的Strict Mode会双重渲染组件,可能导致Framer Motion动画实例冲突。在_app.tsx中移除<React.StrictMode>包裹,测试是否解决问题:
// _app.tsx export default function MyApp({ Component, pageProps }) { return <Component {...pageProps} />; }
内容的提问来源于stack exchange,提问作者Gourav Kumar
相关产品推荐
相关产品推荐

