实现一个 [X, Y] = useDrag(ref, {X: 10, Y: 10})
的元素拖拽逻辑
import { useEffect, useCallback, useReducer } from 'react';
interface DragState {
X: number;
Y: number;
mouseX: number;
mouseY: number;
moving?: boolean;
}
type DispatchType = 'start' | 'move' | 'stop';
interface DispatchAction {
type: DispatchType;
X?: number;
Y?: number;
mouseX?: number;
mouseY?: number;
}
function reducer(state: DragState, action: DispatchAction) {
const { type, X = 0, Y = 0, mouseX = 0, mouseY = 0 } = action;
switch (type) {
case 'start':
return {
X,
Y,
mouseX,
mouseY,
moving: true
};
case 'move':
if (state.moving) {
return {
...state,
X: state.X + mouseX - state.mouseX,
Y: state.Y + mouseY - state.mouseY,
mouseX,
mouseY
};
}
return state;
case 'stop':
return {
...state,
moving: false
};
default:
return {
X: 0,
Y: 0,
mouseX: 0,
mouseY: 0
};
}
}
function useDrag(ref, initState) {
const [state, dispatch] = useReducer(reducer, initState);
const handleStart = useCallback(
e => {
e.preventDefault();
dispatch({
type: 'start',
X: e.currentTarget.offsetLeft,
Y: e.currentTarget.offsetTop,
mouseX: e.pageX,
mouseY: e.pageY
});
},
[ref]
);
const handleMove = useCallback(
e => {
e.preventDefault();
dispatch({
type: 'move',
mouseX: e.pageX,
mouseY: e.pageY
});
},
[ref]
);
const handleStop = useCallback(
e => {
e.preventDefault();
dispatch({ type: 'stop' });
},
[ref]
);
useEffect(() => {
if (!ref) {
return;
}
ref.current.addEventListener('mousedown', handleStart);
document.addEventListener('mousemove', handleMove);
document.addEventListener('mouseup', handleStop);
return () => {
ref.current.removeEventListener('mousedown', handleStart);
document.removeEventListener('mousemove', handleMove);
document.removeEventListener('mouseup', handleStop);
};
}, [ref]);
return [state.X, state.Y];
}
export default useDrag;
import React, { FC, useRef, useEffect } from 'react';
import useDrag from './useDrag';
const DragTest: FC = () => {
const dom = useRef<any>(null);
const [X, Y] = useDrag(dom, { X: 10, Y: 10 });
return (
<div style={{ height: '800px', position: 'relative' }}>
<div
style={{
position: 'absolute',
backgroundColor: 'red',
left: X,
top: Y,
cursor: 'pointer',
width: '100px',
height: '100px'
}}
ref={dom}>
X: {X}, Y: {Y}
</div>
</div>
);
};
export default DragTest;