实现鼠标跟随眼睛动画时getBoundingClientRect空值错误求助
解决React中人脸眼睛跟随鼠标动画的
Uncaught TypeError问题 我参考视频实现人脸眼睛跟随鼠标移动的动画,自定义为1张基底图加1组眼睛,但控制台出现
Uncaught TypeError: Cannot read properties of null (reading 'getBoundingClientRect')错误,请问原因及解决方法?
原代码
import React from "react"; import "./MovingFounder.css"; class MovingFounder extends React.Component { render() { document.addEventListener("mousemove", (e) => { const mouseX = e.clientX; const mouseY = e.clientY; const anchor = document.getElementById('anchor'); const rekt = anchor.getBoundingClientRect(); const anchorX = rekt.left + rekt.width / 2; const anchorY = rekt.top + rekt.height / 2; const angleDeg = angle(mouseX, mouseY, anchorX, anchorY); console.log(angleDeg); const eyes = document.querySelector(".founder-eyes"); eyes.forEach((eye) => { eye.style.transform = `rotate(${angleDeg}deg)`; }) }); function angle(cx, cy, ex, ey) { const dy = ey - cy; const dx = ex - cx; const rad = Math.atan2(dy, dx); const deg = rad * (180 / Math.PI); return deg; } return ( <div class="founder-moving"> <img class="anchor" src="./images/noeyes-founder.png" alt="founder body" /> <div> <img class="founder-eyes" src="./images/founder-eyes.png" alt="founder eyes" /> </div> </div> ); } } export default MovingFounder;
错误原因
- DOM元素匹配失败:你用
getById('anchor')查找元素,但基底图标签用的是class="anchor"而非id="anchor",导致anchor变量为null,调用getBoundingClientRect()直接报错。 - 事件绑定时机错误:在
render()里绑定mousemove事件,每次组件渲染都会重复绑定,且可能在DOM未完全挂载时触发事件,进一步导致元素获取失败。 - 元素遍历逻辑错误:
querySelector(".founder-eyes")仅返回第一个匹配元素,不是数组,无法用forEach遍历,会触发额外错误。 - React语法错误:JSX中需用
className代替class,否则React无法正确解析类名。
解决方法
- 修正DOM选择器:给基底图添加
id="anchor",或改用querySelector(".anchor")匹配类名。 - 规范事件绑定:在
componentDidMount中绑定事件,componentWillUnmount中解绑,避免重复绑定和内存泄漏。 - 使用React Refs:用React内置的refs获取DOM元素,比原生DOM查询更可靠,符合React数据流规范。
- 修正遍历逻辑:单组眼睛直接操作元素即可;多组眼睛改用
querySelectorAll(".founder-eyes")获取元素列表。 - 修复JSX语法:将所有
class替换为className。
修正后的代码
import React from "react"; import "./MovingFounder.css"; class MovingFounder extends React.Component { constructor(props) { super(props); // 创建refs绑定DOM元素 this.anchorRef = React.createRef(); this.eyeRef = React.createRef(); } componentDidMount() { // 组件挂载完成后绑定鼠标事件 document.addEventListener("mousemove", this.handleMouseMove); } componentWillUnmount() { // 组件卸载前解绑事件,避免内存泄漏 document.removeEventListener("mousemove", this.handleMouseMove); } handleMouseMove = (e) => { const mouseX = e.clientX; const mouseY = e.clientY; // 确保元素存在再执行后续逻辑 const anchor = this.anchorRef.current; if (!anchor) return; const rekt = anchor.getBoundingClientRect(); const anchorX = rekt.left + rekt.width / 2; const anchorY = rekt.top + rekt.height / 2; const angleDeg = this.angle(mouseX, mouseY, anchorX, anchorY); // 设置眼睛旋转角度 const eye = this.eyeRef.current; if (eye) { eye.style.transform = `rotate(${angleDeg}deg)`; } }; angle(cx, cy, ex, ey) { const dy = ey - cy; const dx = ex - cx; const rad = Math.atan2(dy, dx); const deg = rad * (180 / Math.PI); return deg; } render() { return ( <div className="founder-moving"> <img ref={this.anchorRef} className="anchor" src="./images/noeyes-founder.png" alt="founder body" /> <div> <img ref={this.eyeRef} className="founder-eyes" src="./images/founder-eyes.png" alt="founder eyes" /> </div> </div> ); } } export default MovingFounder;
内容的提问来源于stack exchange,提问作者Burmpf
相关产品推荐
相关产品推荐

