min*_*hah 1 reactjs styled-components react-hooks
我对钩子还很陌生,我正在尝试实现一个拖放容器组件,该组件在整个鼠标移动过程中都处理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
Run Code Online (Sandbox Code Playgroud)
样式化的组件文件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
}
Run Code Online (Sandbox Code Playgroud)
因此,我最初从父级传递了初始值。我认为我没有正确处理useEffect / useState,并且获取信息的速度不够快。
如果有人可以帮助我解决该问题,我将非常感激。再次道歉,但是我对使用挂钩非常陌生。
谢谢 :)
理想情况下,由于setState是异步的,因此您可以将所有状态都移动到一个状态object(如中间示例所示)。然后,您可以利用setState回调来确保每个event listener和event callback正在使用的值都是最新的setState。
我认为该篇中篇文章中的示例具有相同的跳跃问题(这可能是示例视频缓慢移动对象的原因),但是如果没有有效的示例,很难说。这就是说,要解决这个问题,我删除了originalX,originalY,lastTranslateX,lastTranslateY因为不是在需要的时候,因为我们利用价值setState的回调。
此外,我将event listeners/ 简化callbacks为:
mousedown=>鼠标左键按住设置为isDraggingtruemousemove=>鼠标移动更新translateX以及translateY通过clientX和clientY更新mouseup=>鼠标左键释放设置isDragging为false。这将确保只有一个事件侦听器实际上是转化x和y价值。
如果要利用此示例包含多个圆圈,则需要重用下面的组件,或者使用useRef并利用refs来移动选定的圆圈;但是,这超出了您原始问题的范围。
最后,我还修正了styled-components通过重组弃用问题styled.div.data.attr是一个function一个返回style与财产CSS,而不是一个object一个style是一个属性function是回报CSS。
不推荐使用:
styled.div.attrs({
style: ({ x, y, radius }) => ({
transform: `translate(${x - radius}px, ${y - radius}px)`
})
})`
Run Code Online (Sandbox Code Playgroud)
更新:
styled.div.attrs(({ x, y, radius }) => ({
style: {
transform: `translate(${x - radius}px, ${y - radius}px)`
}
}))`
Run Code Online (Sandbox Code Playgroud)
工作示例:
零件/圆
import styled from "styled-components";
const Circle = styled.div.attrs(({ x, y, radius }) => ({
style: {
transform: `translate(${x - radius}px, ${y - radius}px)`
}
}))`
cursor: grab;
position: absolute;
width: 25px;
height: 25px;
background-color: red;
border-radius: 50%;
${({ isDragging }) =>
isDragging &&
`
opacity: 0.8;
cursor: grabbing;
`}
`;
export default Circle;
Run Code Online (Sandbox Code Playgroud)
组件/可拖动
import React, { useState, useEffect, useCallback } from "react";
import PropTypes from "prop-types";
import Circle from "../Circle";
const Draggable = ({ position, radius }) => {
const [state, setState] = useState({
isDragging: false,
translateX: position.x,
translateY: position.y
});
// mouse move
const handleMouseMove = useCallback(
({ clientX, clientY }) => {
if (state.isDragging) {
setState(prevState => ({
...prevState,
translateX: clientX,
translateY: clientY
}));
}
},
[state.isDragging]
);
// mouse left click release
const handleMouseUp = useCallback(() => {
if (state.isDragging) {
setState(prevState => ({
...prevState,
isDragging: false
}));
}
}, [state.isDragging]);
// mouse left click hold
const handleMouseDown = useCallback(() => {
setState(prevState => ({
...prevState,
isDragging: true
}));
}, []);
// adding/cleaning up mouse event listeners
useEffect(() => {
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
}, [handleMouseMove, handleMouseUp]);
return (
<Circle
isDragging={state.isDragging}
onMouseDown={handleMouseDown}
radius={radius}
x={state.translateX}
y={state.translateY}
/>
);
};
// prop type schema
Draggable.propTypes = {
position: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number
}),
radius: PropTypes.number
};
// default props if none are supplied
Draggable.defaultProps = {
position: {
x: 20,
y: 20
},
radius: 10,
};
export default Draggable;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
804 次 |
| 最近记录: |