效果:

实现可视化编辑器,第一步难点 是 拖拽
提示:链接和图片默认是可拖动的,不需要draggable属性。
在拖放操作的不同阶段使用并可能发生许多事件属性:
-
在可拖动目标上触发的事件(源元素):
ondragstart - 当用户开始拖动元素时触发
ondrag - 拖动元素时触发
ondragend - 在用户完成拖动元素时触发
-
在放置目标上触发的事件:
ondragenter - 当被拖动的元素进入放置目标时触发
ondragover - 当被拖动的元素超过放置目标时触发
ondragleave - 当被拖动的元素离开放置目标时触发
ondrop - 当被拖动的元素放在放置目标上时触发

这种代码结构更加清楚, demo 和content是俩个完全没有关系的兄弟div
现在需求的 红色拖拽到蓝色中, 这里的方法是定位
情况一:
handleDragStart(e, 1)}
style={{
width: '100px',
height: '100px',
backgroundColor: 'red',
margin: '30px',
}}
>
demo2
const handleDragStart = (e: DragEvent, id: number) => {
e.dataTransfer.setData('text/plain', id.toString()); // 存储id, 和 data-XX一个道理
};
{
width: '300px',
height: '300px',
margin: '30px',
backgroundColor: 'blue',
position: 'relative',
}}
>
content
{demos.map((demo) => (
{
width: '100px',
height: '100px',
backgroundColor: 'red',
position: 'absolute',
left: `${demo.x}px`,
top: `${demo.y}px`,
}}
>
demo {demo.id}
))}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
const handleDrop = (e: DragEvent) => {
e.preventDefault();
const clientX = e.clientX;
const clientY = e.clientY;
const contentStyle = document
.getElementById('content')
.getBoundingClientRect();
const x = clientX - contentStyle.left
const y = clientY - contentStyle.top;
const newDemo: Demo = { x, y, id: +new Date() };
setDemos([...demos, newDemo]);
};
上面代码测试结果:

代码测试 ,会有一些偏差,原因是 鼠标拖拽的位置的不是红色div的左上角顶点, 这样的就不会发生偏移, 但是实际情况无法保证每次都是拖拽顶点, 那需要在开始拖拽的计算的鼠标相对于红色div的偏移值
情况二:完整的代码
import React, { useState, DragEvent, useEffect, MouseEvent } from 'react';
interface Demo {
id: number;
x: number;
y: number;
}
const App: React.FC = () => {
const [demos, setDemos] = useState([]);
const handleDragStart = (e: DragEvent, 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) => {
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) => {
e.preventDefault();
};
const onMouseDown = (e: MouseEvent) => {
console.info('onMouseDown', e);
};
const onMouseUp = (e: MouseEvent) => {
console.info('onMouseUp', e);
};
const onDragEnd = (e: MouseEvent) => {
console.info('onDragEnd', e);
};
return (
handleDragStart(e, 1)}
onDragEnd={onDragEnd}
style={{
width: '100px',
height: '100px',
backgroundColor: 'red',
margin: '30px',
}}
>
demo2
{
width: '300px',
height: '300px',
margin: '30px',
backgroundColor: 'blue',
position: 'relative',
}}
>
content
{demos.map((demo) => (
{
width: '100px',
height: '100px',
backgroundColor: 'red',
position: 'absolute',
left: `${demo.x}px`,
top: `${demo.y}px`,
}}
>
demo {demo.id}
))}
);
};
export default App;

- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104