React Konva:移除上方图形后onMouseOver未触发的问题解决
React Konva重叠矩形悬停触发问题解决
我有一个包含两个重叠<Rect/>组件的React Konva项目:
- 底部的
<Rect/>组件在鼠标悬停时会改变颜色,代码如下:
export const BottomSquare = () => { const [color, setColor] = useState("green"); return ( <Rect width={100} height={100} opacity={1} fill={color} onMouseOver={() => { setColor("blue"); }} onMouseLeave={() => { setColor("green"); }} /> ); };
- 上方的
<Rect/>组件(尺寸小于底部组件)点击后会移动到远离底部组件的位置,代码如下:
export const TopSquare = () => { const [position, setPosition] = useState({ x: 0, y: 0 }); return ( <Rect {...position} width={50} height={50} opacity={1} fill="grey" onClick={(event) => { setPosition({ x: 100, y: 100 }); }} /> ); };
悬停在上方<Rect/>时不会触发底部组件的变色逻辑,这符合预期。但点击上方组件使其移开后,光标处于底部组件区域内时,onMouseOver逻辑不会触发,除非移动至少1像素光标,这更像是onMouseMove的行为。尝试使用onMouseEnter和onMouseMove,问题依旧。
解决方案
这个问题的核心是Konva的事件系统依赖于鼠标位置的变化来触发悬停类事件。当上方矩形移开后,光标位置没有改变,Konva不会主动检测当前光标下的新元素,自然不会触发onMouseOver或onMouseEnter。
我们可以通过两种思路解决:
思路一:手动触发底部组件的悬停事件
通过组件间通信,在上方矩形移动后主动触发底部组件的悬停逻辑:
修改后的代码
BottomSquare.tsx
import { useRef, useState, forwardRef, useImperativeHandle } from "react"; import { Rect } from "react-konva"; // 使用forwardRef暴露内部方法给父组件 const BottomSquare = forwardRef((_, ref) => { const [color, setColor] = useState("green"); const rectRef = useRef<Rect>(null); const handleHover = () => { setColor("blue"); }; const handleLeave = () => { setColor("green"); }; // 暴露触发悬停的方法 useImperativeHandle(ref, () => ({ triggerHover: () => { if (rectRef.current) { rectRef.current.fire("mouseenter"); } }, })); return ( <Rect ref={rectRef} width={100} height={100} opacity={1} fill={color} onMouseOver={handleHover} onMouseLeave={handleLeave} onMouseEnter={handleHover} // 同时监听mouseenter增强兼容性 /> ); }); export default BottomSquare;
TopSquare.tsx
import { useState } from "react"; import { Rect } from "react-konva"; interface TopSquareProps { onMove: () => void; } export default function TopSquare({ onMove }: TopSquareProps) { const [position, setPosition] = useState({ x: 0, y: 0 }); const handleClick = () => { setPosition({ x: 100, y: 100 }); onMove(); }; return ( <Rect {...position} width={50} height={50} opacity={1} fill="grey" onClick={handleClick} /> ); }
App.tsx
import { useRef } from "react"; import { Stage, Layer } from "react-konva"; import BottomSquare from "./BottomSquare"; import TopSquare from "./TopSquare"; export default function App() { const bottomSquareRef = useRef<{ triggerHover: () => void }>(null); return ( <Stage width={window.innerWidth} height={window.innerHeight}> <Layer> <BottomSquare ref={bottomSquareRef} /> <TopSquare onMove={() => { bottomSquareRef.current?.triggerHover(); }} /> </Layer> </Stage> ); }
思路二:模拟鼠标移动事件
不需要修改底部组件,在上方矩形移动后,模拟一次鼠标移动事件,让Konva重新检测光标下的元素:
// 修改TopSquare的点击事件 const handleClick = (e) => { setPosition({ x: 100, y: 100 }); // 获取当前光标位置 const { clientX, clientY } = e.evt; // 触发舞台的mousemove事件 e.target.getStage().container().dispatchEvent( new MouseEvent('mousemove', { clientX, clientY, bubbles: true, cancelable: true }) ); };
内容的提问来源于stack exchange,提问作者Riexn
相关产品推荐
相关产品推荐

