Reactjs 如何在React中处理父级中的mouseMove事件?
我尝试在React中实现拖放,并使用SVG元素。问题是如果用户移动鼠标太快,Reactjs 如何在React中处理父级中的mouseMove事件?,reactjs,svg,draggable,react-hooks,mousemove,Reactjs,Svg,Draggable,React Hooks,Mousemove,我尝试在React中实现拖放,并使用SVG元素。问题是如果用户移动鼠标太快,mouseMove不会被触发。它基本上经常会失去牵引力。要解决这个问题,我想我需要在父对象中处理mouseMove,但不确定如何使用React来处理。我尝试了几种不同的方法,但都没有用 我使用ref在父对象上尝试了addEventListener('mousemove',…),但问题是clientX与当前组件的坐标系不同。此外,事件处理程序无权从组件访问任何状态(带有箭头函数的事件)。它维护对任何状态的过时引用 我尝试在
mouseMove
不会被触发。它基本上经常会失去牵引力。要解决这个问题,我想我需要在父对象中处理mouseMove
,但不确定如何使用React来处理。我尝试了几种不同的方法,但都没有用
我使用ref在父对象上尝试了addEventListener('mousemove',…)
,但问题是clientX
与当前组件的坐标系不同。此外,事件处理程序无权从组件访问任何状态(带有箭头函数的事件)。它维护对任何状态的过时引用
我尝试在父对象上的上下文中设置clientX
和clientY
,然后从DragMe
组件中将其拉入,但由于某种奇怪的原因,它第一次总是未定义
,即使我给它一个默认值
以下是我正在使用的代码:
const DragMe = ({ x = 50, y = 50, r = 10 }) => {
const [dragging, setDragging] = useState(false)
const [coord, setCoord] = useState({ x, y })
const [offset, setOffset] = useState({ x: 0, y: 0 })
const [origin, setOrigin] = useState({ x: 0, y: 0 })
const xPos = coord.x + offset.x
const yPos = coord.y + offset.y
const transform = `translate(${xPos}, ${yPos})`
const fill = dragging ? 'red' : 'green'
const stroke = 'black'
const handleMouseDown = e => {
setDragging(true)
setOrigin({ x: e.clientX, y: e.clientY })
}
const handleMouseMove = e => {
if (!dragging) { return }
setOffset({
x: e.clientX - origin.x,
y: e.clientY - origin.y,
})
}
const handleMouseUp = e => {
setDragging(false)
setCoord({ x: xPos, y: yPos })
setOrigin({ x: 0, y: 0 })
setOffset({ x: 0, y: 0 })
}
return (
<svg style={{ userSelect: 'none' }}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseUp}
>
<circle transform={transform} cx="0" cy="0" r={r} fill={fill} stroke={stroke} />
</svg>
)
}
const DragMe=({x=50,y=50,r=10})=>{
常量[Draging,SetDraging]=useState(false)
const[coord,setCoord]=useState({x,y})
const[offset,setOffset]=useState({x:0,y:0})
const[origin,setOrigin]=useState({x:0,y:0})
常数xPos=coord.x+offset.x
常数yPos=coord.y+offset.y
const transform=`translate(${xPos},${yPos})`
常量填充=拖动?'red':'green'
常量笔划=‘黑色’
常量handleMouseDown=e=>{
设置拖动(真)
setOrigin({x:e.clientX,y:e.clientY})
}
常量handleMouseMove=e=>{
如果(!拖动){return}
设置偏移量({
x:e.clientX-origin.x,
y:e.clientY-origin.y,
})
}
const handleMouseUp=e=>{
设置拖动(错误)
setCoord({x:xPos,y:yPos})
setOrigin({x:0,y:0})
setOffset({x:0,y:0})
}
返回(
)
}
经过多次实验后,我能够将EventListener
添加到父画布。我发现我需要useRef
以允许mousemove
处理程序查看当前状态。我以前遇到的问题是,handleParentMouseMove
处理程序对状态的引用已过时,并且从未看到startDragPos
这就是我想出的解决办法。如果有人知道一个方法来清理这将不胜感激
const DragMe = ({ x = 50, y = 50, r = 10, stroke = 'black' }) => {
// `mousemove` will not generate events if the user moves the mouse too fast
// because the `mousemove` only gets sent when the mouse is still over the object.
// To work around this issue, we `addEventListener` to the parent canvas.
const canvasRef = useContext(CanvasContext)
const [dragging, setDragging] = useState(false)
// Original position independent of any dragging. Updated when done dragging.
const [originalCoord, setOriginalCoord] = useState({ x, y })
// The distance the mouse has moved since `mousedown`.
const [delta, setDelta] = useState({ x: 0, y: 0 })
// Store startDragPos in a `ref` so handlers always have the latest value.
const startDragPos = useRef({ x: 0, y: 0 })
// The current object position is the original starting position + the distance
// the mouse has moved since the start of the drag.
const xPos = originalCoord.x + delta.x
const yPos = originalCoord.y + delta.y
const transform = `translate(${xPos}, ${yPos})`
// `useCallback` is needed because `removeEventListener`` requires the handler
// to be the same as `addEventListener`. Without `useCallback` React will
// create a new handler each render.
const handleParentMouseMove = useCallback(e => {
setDelta({
x: e.clientX - startDragPos.current.x,
y: e.clientY - startDragPos.current.y,
})
}, [])
const handleMouseDown = e => {
setDragging(true)
startDragPos.current = { x: e.clientX, y: e.clientY }
canvasRef.current.addEventListener('mousemove', handleParentMouseMove)
}
const handleMouseUp = e => {
setDragging(false)
setOriginalCoord({ x: xPos, y: yPos })
startDragPos.current = { x: 0, y: 0 }
setDelta({ x: 0, y: 0 })
canvasRef.current.removeEventListener('mousemove', handleParentMouseMove)
}
const fill = dragging ? 'red' : 'green'
return (
<svg style={{ userSelect: 'none' }}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
>
<circle transform={transform} cx="0" cy="0" r={r} fill={fill} stroke={stroke} />
</svg>
)
}
const DragMe=({x=50,y=50,r=10,stroke='black'})=>{
//如果用户移动鼠标太快,`mousemove`将不会生成事件
//因为“mousemove”只有在鼠标仍在对象上时才会被发送。
//为了解决这个问题,我们在父画布中添加了“EventListener”。
const canvasRef=useContext(CanvasContext)
常量[Draging,SetDraging]=useState(false)
//原始位置独立于任何拖动。拖动完成后更新。
const[originalcord,setoriginalcord]=useState({x,y})
//自“mousedown”以来鼠标移动的距离。
const[delta,setDelta]=useState({x:0,y:0})
//将startDragPos存储在'ref'中,以便处理程序始终具有最新的值。
const startDragPos=useRef({x:0,y:0})
//当前对象位置是原始起始位置+距离
//自开始拖动后,鼠标已移动。
常量xPos=原始命令x+增量x
常量yPos=原始命令y+增量y
const transform=`translate(${xPos},${yPos})`
//需要使用“useCallback”,因为“removeEventListener”需要处理程序
//与“addEventListener”相同。如果没有“useCallback”,则
//在每次渲染时创建一个新的处理程序。
const handleParentMouseMove=useCallback(e=>{
塞德尔塔({
x:e.clientX-startDragPos.current.x,
y:e.clientY-startDragPos.current.y,
})
}, [])
常量handleMouseDown=e=>{
设置拖动(真)
startDragPos.current={x:e.clientX,y:e.clientY}
canvasRef.current.addEventListener('mousemove',handleParentMouseMove)
}
const handleMouseUp=e=>{
设置拖动(错误)
setoriginalcord({x:xPos,y:yPos})
startDragPos.current={x:0,y:0}
setDelta({x:0,y:0})
canvasRef.current.removeEventListener('mousemove',handleParentMouseMove)
}
常量填充=拖动?'red':'green'
返回(
)
}
仅隔离在此处工作的代码:
首先,在render()
函数中找出您的顶级父级是什么,在这里您可能会遇到如下情况:
render() {
return (
<div ref={(parent_div) => { this.parent_div = parent_div }}}>
// other div stuff
</div>
)
}
尝试添加e.preventDefault();在每个事件方法中,也尝试添加e.stopPropagation()
我最初尝试过,但问题不是其他事件。问题是mousemove
仅在鼠标仍在对象上方时发出。当鼠标移动过快时,光标将在更新其位置之前位于对象外部。
this.parent_div.addEventListener('mousemove', function (event) {
console.log(event.clientX)
}