我对钩子还很陌生,我正在尝试实现一个拖放容器组件,该组件在整个鼠标移动过程中都处理onDragStart,onDrag和onDragEnd函数。我一直在尝试使用钩子复制此处找到的代码:https://medium.com/@crazypixel/mastering-drag-drop-with-reactjs-part-01-39bed3d40a03
我几乎可以使用下面的代码来工作。它使用样式化的组件进行动画处理。问题是,仅当您缓慢移动鼠标时它才起作用。如果您快速移动鼠标,则会将SVG或该div中包含的所有内容抛出屏幕。
我有一个看起来像
的component.js
文件
import React, { useState, useEffect, useCallback } from 'react';
import { Container } from './style'
const Draggable = ({children, onDragStart, onDrag, onDragEnd, xPixels, yPixels, radius}) => {
const [isDragging, setIsDragging] = useState(false);
const [original, setOriginal] = useState({
x: 0,
y: 0
});
const [translate, setTranslate] = useState({
x: xPixels,
y: yPixels
});
const [lastTranslate, setLastTranslate] = useState({
x: xPixels,
y: yPixels
});
useEffect(() =>{
setTranslate({
x: xPixels,
y: yPixels
});
setLastTranslate({
x: xPixels,
y: yPixels
})
}, [xPixels, yPixels]);
const handleMouseMove = useCallback(({ clientX, clientY }) => {
if (!isDragging) {
return;
}
setTranslate({
x: clientX - original.x + lastTranslate.x,
y: clientY - original.y + lastTranslate.y
});
}, [isDragging, original, lastTranslate, translate]);
const handleMouseUp = useCallback(() => {
window.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('mouseup', handleMouseUp);
setOriginal({
x:0,
y:0
});
setLastTranslate({
x: translate.x,
y: translate.y
});
setIsDragging(false);
if (onDragEnd) {
onDragEnd();
}
}, [isDragging, translate, lastTranslate]);
useEffect(() => {
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('mouseup', handleMouseUp);
return () => {
window.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('mouseup', handleMouseUp)
};
}, [handleMouseMove, handleMouseUp]);
const handleMouseDown = ({ clientX, clientY }) =>{
if (onDragStart) {
onDragStart();
}
setOriginal({
x: clientX,
y: clientY
});
setIsDragging(true);
};
return(
<Container
onMouseDown={handleMouseDown}
x={translate.x}
y={translate.y}
{...{radius}}
isDragging={isDragging}
>
{children}
</Container>
)
};
export default Draggable
样式化的组件文件styled.js
如下所示:
import styled from 'styled-components/macro';
const Container = styled.div.attrs({
style: ({x,y, radius}) => ({
transform: `translate(${x - radius}px, ${y - radius}px)`
})
})`
//cursor: grab;
position: absolute;
${({isDragging}) =>
isDragging && `
opacity: 0.8
cursor: grabbing
`}
`;
export {
Container
}
因此,我最初从父级传入了初始值。我认为我没有正确处理useEffect / useState,并且获取信息的速度不够快。
如果有人可以帮助我解决该问题,我将非常感激。再次道歉,但是我对使用挂钩非常陌生。
谢谢:)
答案 0 :(得分:2)
理想情况下,由于setState
是异步的,因此您需要将所有状态都移到一个object
中(如中间示例所示)。然后,您可以利用setState callback确保在调用event listener
时,每个event callback
和setState
所使用的值都是最新的。
我认为那篇中篇文章中的示例存在相同的跳跃问题(这可能是示例视频缓慢移动对象的原因),但是如果没有有效的示例,这很难说。也就是说,为了解决此问题,我删除了originalX
,originalY
,lastTranslateX
,lastTranslateY
值,因为我们利用{{1 }}回调。
此外,我将setState
/ event listeners
简化为:
callbacks
=>鼠标左键按住mousedown
true isDragging
=>鼠标移动通过mousemove
和translateX
更新来更新translateY
和clientX
clientY
=>鼠标左键释放将mouseup
设置为false。 这可确保只有一个事件侦听器实际上在转换isDragging
和x
的值。
如果要利用此示例包含多个圆圈,则需要使用y
并利用useRef
移动选定的圆圈;但是,这超出了您原始问题的范围。
最后,我还通过将refs
重组为一个styled-components
并返回styled.div.data.attr
属性和function
而不是{具有style
属性的CSS
,该属性是返回object
的{{1}}。
已弃用:
style
已更新:
function
工作示例:
组件/容器
CSS
组件/可拖动
styled.div.attrs({
style: ({ x, y, radius }) => ({
transform: `translate(${x - radius}px, ${y - radius}px)`
})
})`