按钮悬停气泡动画仅保存JSX文件后生效,刷新后失效求助
问题分析与解决方案
你这个问题核心是DOM元素获取时机错误:页面刷新时,document.querySelector代码在React组件还没完成渲染、按钮元素未挂载到DOM时就执行了,导致找不到元素,动画失效;而VS Code保存后是热重载,此时组件已经渲染完成,DOM元素存在,所以查询成功,动画正常。
解决步骤
- 使用React的
Ref获取DOM元素,替代全局document.querySelector,这是React操作DOM的标准方式 - 在组件挂载完成后初始化
HoverButton,通过useEffect钩子确保DOM渲染完毕再执行动画逻辑 - 添加事件清理逻辑,避免组件卸载后残留事件导致内存泄漏
修改后的完整代码
import './App.css'; import CoffeeSteamingBG from './CoffeeSteamingBG.jpg' import Cart from './cart.svg' import { gsap } from 'gsap'; import { useEffect, useRef } from 'react'; function App() { return ( <> <FirstHeader /> <IndexHome /> <CoolBtns /> </> ) } function FirstHeader() { return ( <header> <nav className='FirstNav'> <div className='OuterDivCJLogo'> <span className='FirstCJLogo'>Cuppa Java</span> </div> </nav> </header> ) } function IndexHome() { return ( <> <div className='slogan'> <h1><em>The Art of Coffee Elevated</em></h1> </div> </> ) } function CoolBtns() { // 定义ref指向三个按钮 const btn1Ref = useRef(null); const btn2Ref = useRef(null); const btn3Ref = useRef(null); // 保存HoverButton实例用于清理事件 const hoverInstances = useRef([]); useEffect(() => { // 组件挂载后初始化动画 const buttons = [btn1Ref.current, btn2Ref.current, btn3Ref.current]; if (buttons.every(btn => btn)) { hoverInstances.current = buttons.map(btn => new HoverButton(btn)); } // 组件卸载时清理事件监听 return () => { hoverInstances.current.forEach(instance => instance.destroy()); }; }, []); // 空依赖确保只执行一次 return ( <div className='FunNew'> <ul className='Funul'> <li className='Funli'> <button ref={btn1Ref} className='FunBtn F1'><a>Menu</a></button> </li> <li className='Funli'> <button ref={btn2Ref} className='FunBtn F2'><a>Bistro</a></button> </li> <li className='Funli'> <button ref={btn3Ref} className='FunBtn F3'><a>Cart</a></button> </li> </ul> </div> ) } class HoverButton { constructor(el) { this.el = el; this.hover = false; // 绑定this确保事件能正确移除 this.onMouseMove = this.onMouseMove.bind(this); this.calculatePosition = this.calculatePosition.bind(this); this.calculatePosition(); this.attachEventsListener(); } attachEventsListener() { window.addEventListener('mousemove', this.onMouseMove); window.addEventListener('resize', this.calculatePosition); } // 添加销毁方法清理事件 destroy() { window.removeEventListener('mousemove', this.onMouseMove); window.removeEventListener('resize', this.calculatePosition); } calculatePosition() { gsap.set(this.el, { x: 0, y: 0, scale: 1 }); if(this.el) { const box = this.el.getBoundingClientRect(); this.x = box.left + box.width * 0.5; this.y = box.top + box.height * 0.5; this.width = box.width; this.height = box.height; } } onMouseMove(e) { let hover = false; let hoverArea = this.hover ? 0.7 : 0.5; let x = e.clientX - this.x; let y = e.clientY - this.y; let distance = Math.sqrt(x * x + y * y); if (distance < this.width * hoverArea) { hover = true; if (!this.hover) { this.hover = true; } this.onHover(e.clientX, e.clientY); } if (!hover && this.hover) { this.onLeave(); this.hover = false; } } onHover(x, y) { gsap.to(this.el, { x: (x - this.x) * 0.4, y: (y - this.y) * 0.4, scale: 1.15, ease: 'power2.out', duration: 0.4 }); this.el.style.zIndex = 10; } onLeave() { gsap.to(this.el, { x: 0, y: 0, scale: 1, ease: 'elastic.out(1.2, 0.4)', duration: 0.7 }); this.el.style.zIndex = 1; } } export default App
说明
修改后,React会先完成组件渲染并挂载DOM元素,再通过useEffect执行动画初始化,确保按钮元素存在;同时添加了事件清理逻辑,避免内存泄漏。页面刷新和热重载时动画都会正常工作。
内容的提问来源于stack exchange,提问作者42Zach
相关产品推荐
相关产品推荐

