代码
import React, {useState,DragEvent,useRef,useEffect,MouseEvent,
} from 'react';
// import { throttle } from 'lodash';interface Demo {id: number;x: number;y: number;
}const App: React.FC = () => {const [demos, setDemos] = useState<Demo[]>([]);// let currentDiv: HTMLDivElement | null = null;const divRef = useRef<HTMLDivElement | null>(null);const handleDragStart = (e: DragEvent<HTMLDivElement>, id: number) => {e.dataTransfer.setData('text/plain', id.toString());const offsetX = e.clientX - e.currentTarget.getBoundingClientRect().left;const offsetY = e.clientY - e.currentTarget.getBoundingClientRect().top;e.dataTransfer.setData('offsetX', offsetX.toString());e.dataTransfer.setData('offsetY', offsetY.toString());};const handleDrop = (e: DragEvent<HTMLDivElement>) => {e.preventDefault();const clientX = e.clientX;const clientY = e.clientY;const contentStyle = document.getElementById('content').getBoundingClientRect();const offsetX = e.dataTransfer.getData('offsetX');const offsetY = e.dataTransfer.getData('offsetY');const x = clientX - contentStyle.left - offsetX;const y = clientY - contentStyle.top - offsetY;const newDemo: Demo = { x, y, id: +new Date() };setDemos([...demos, newDemo]);};const handleDragOver = (e: DragEvent<HTMLDivElement>) => {e.preventDefault();};const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {e.stopPropagation();divRef.current = e.currentTarget;let { top, left } = divRef.current.style;console.info('top, left', top, left);// 如果直接修改属性,值的类型会变为字符串,所以要转为数值型const startTop = top ? Number(top.replace('px', '')) : 0;const startLeft = left ? Number(left.replace('px', '')) : 0;const startY = e.clientY;const startX = e.clientX;const move = (moveEvent: { clientX: number; clientY: number }) => {if (!divRef.current) return; // 检查divRef.current是否存在const currX = moveEvent.clientX;const currY = moveEvent.clientY;console.info('move', currX, currY);const newTop = currY - startY + startTop;const newLeft = currX - startX + startLeft;// 获取父元素的边界const parent = divRef.current.parentElement;if (!parent) return;const parentRect = parent.getBoundingClientRect();const divRect = divRef.current.getBoundingClientRect();// 限制div不超过父元素的边界const maxTop = parentRect.height - divRect.height;const maxLeft = parentRect.width - divRect.width;top = `${Math.min(Math.max(newTop, 0), maxTop)}px`;left = `${Math.min(Math.max(newLeft, 0), maxLeft)}px`;// 修改当前组件样式divRef.current.style.left = left;divRef.current.style.top = top;};const up = () => {document.removeEventListener('mousemove', move);document.removeEventListener('mouseup', up);divRef.current = null; // 清除对元素的引用};document.addEventListener('mousemove', move);document.addEventListener('mouseup', up);};return (<div><divid="demo"draggableonDragStart={(e) => handleDragStart(e, 1)}style={{width: '100px',height: '100px',backgroundColor: 'red',margin: '30px',cursor: 'pointer',}}>demo2</div><divid="content"onDrop={handleDrop}onDragOver={handleDragOver}style={{width: '300px',height: '300px',margin: '30px',backgroundColor: 'blue',position: 'relative',}}>content{demos.map((demo) => (<divonMouseDown={handleMouseDown}key={demo.id}style={{width: '100px',height: '100px',backgroundColor: 'red',cursor: 'pointer',position: 'absolute',left: `${demo.x}px`,top: `${demo.y}px`,}}>demo {demo.id}</div>))}</div></div>);
};export default App;