本文直接贴全部代码,详情内容和gif动图效果可直接点击转载的网站观看:react 实现div缩放、旋转、拖拽的9个控制点 - JavaShuo
import React, {Component, useState, useRef, useCallback} from 'react';
/**
* 元素变化。 方法放在组件外部或者其余地方。
* @param direction 方向 // move 移动 / 'e', 'w', 's', 'n', 'ne', 'nw', 'se', 'sw'
* @param oriStyle 元素的属性 width height top left
* @param oriPos 鼠标按下时所记录的坐标
* @param e 事件event
*/
function transform(direction, oriPos, e) {
const style = {...oriPos.current};
const offsetX = e.clientX - oriPos.current.cX;
const offsetY = e.clientY - oriPos.current.cY;
switch (direction.current) {
// 东
case 'e':
// 向右拖拽添加宽度
style.width += offsetX;
return style;
// 西
case 'w':
// 增长宽度、位置同步左移
style.width -= offsetX;
style.left += offsetX;
return style;
// 南
case 's':
style.height += offsetY;
return style;
// 北
case 'n':
style.height -= offsetY;
style.top += offsetY;
break;
// 东北
case 'ne':
style.height -= offsetY;
style.top += offsetY;
style.width += offsetX;
break;
// 西北
case 'nw':
style.height -= offsetY;
style.top += offsetY;
style.width -= offsetX;
style.left += offsetX;
break;
// 东南
case 'se':
style.height += offsetY;
style.width += offsetX;
break;
// 西南
case 'sw':
style.height += offsetY;
style.width -= offsetX;
style.left += offsetX;
break;
default:
// 元素当前位置 + 偏移量
// eslint-disable-next-line no-case-declarations
const top = oriPos.current.top + offsetY;
// eslint-disable-next-line no-case-declarations
const left = oriPos.current.left + offsetX;
// 限制必须在这个范围内移动 画板的高度-元素的高度
style.top = Math.max(0, Math.min(top, 500 - style.height));
style.left = Math.max(0, Math.min(left, 500 - style.width));
break;
}
return style;
}
// 东南西北, 东北、西北、东南、西南
// const points = ['e', 'w', 's', 'n', 'ne', 'nw', 'se', 'sw']
const points = ['ne', 'nw', 'se', 'sw'];
function Drawing() {
// 画板的
const wrapStyle = {
left: 100,
top: 100,
width: 500,
height: 500
};
const [style, setStyle] = useState({
left: 100,
top: 100,
width: 100,
height: 100
});
// 初始数据, 由于不须要从新render 因此用 useRef
const oriPos = useRef({
top: 0, // 元素的坐标
left: 0,
cX: 0, // 鼠标的坐标
cY: 0
});
const isDown = useRef(false);
// 鼠标被按下
const onMouseDown = useCallback((dir, e) => {
// 阻止事件冒泡
e.stopPropagation();
// 保存方向。
points.current = dir;
isDown.current = true;
// 而后鼠标坐标是
const cY = e.clientY; // clientX 相对于可视化区域
const cX = e.clientX;
oriPos.current = {
...style,
cX,
cY
};
});
// 鼠标移动
const onMouseMove = useCallback(e => {
// 判断鼠标是否按住
if (!isDown.current) return;
const newStyle = transform(points, oriPos, e);
setStyle(newStyle);
});
// 鼠标被抬起
const onMouseUp = useCallback(e => {
// 阻止事件冒泡
e.stopPropagation();
isDown.current = false;
});
return (
<div style={wrapStyle} className="drawing-wrap" onMouseDown={e => onMouseDown(_, e)} onMouseUp={onMouseUp} onMouseMove={onMouseMove}>
<div className="drawing-item" style={style}>
{points.map(item => (
<div onMouseDown={e => onMouseDown(item, e)} onMouseMove={onMouseMove} key={item} className={`control-point point-${item}`}></div>
))}
</div>
</div>
);
}
export default Drawing;
.drawing-wrap{
width: 500px;
height: 500px;
border: 1px solid red ;
position: relative;
}
.drawing-item {
cursor: move;
width: 100px;
height: 100px;
background-color: #ccc;
position: absolute;
top: 100px;
left: 100px;
box-sizing: border-box;
}
.control-point{
position: absolute;
box-sizing: border-box;
display: inline-block;
background: #fff;
border: 1px solid #c0c5cf;
box-shadow: 0 0 2px 0 rgba(86, 90, 98, .2);
border-radius: 6px;
padding: 5px;
margin-top: -5px !important;
margin-left: -5px !important;
user-select: none; // 注意禁止鼠标选中控制点元素,否则拖拽事件可能会所以被中断
}
.control-point.point-e{
cursor: ew-resize;
left: 100%;
top: 50%;
margin-left: 1px;
opacity: 0;
}
.control-point.point-n{
cursor: ns-resize;
left: 50%;
margin-top: -1px;
opacity: 0;
}
.control-point.point-s{
cursor: ns-resize;
left: 50%;
top: 100%;
margin-top: 1px;
opacity: 0;
}
.control-point.point-w{
cursor: ew-resize;
top: 50%;
left: 0;
margin-left: -1px;
opacity: 0;
}
.control-point.point-ne {
cursor: nesw-resize;
left: 100%;
margin-top: -1px;
margin-left: 1px;
opacity: 0;
}
.control-point.point-nw {
cursor: nwse-resize;
top: 0;
left: 0;
margin-left: -1px;
margin-top: -1px;
opacity: 0;
}
.control-point.point-se {
cursor: nwse-resize;
left: 100%;
top: 100%;
margin-left: 1px;
margin-top: 1px;
opacity: 0;
}
.control-point.point-sw {
cursor: nesw-resize;
top: 100%;
left: 0;
margin-left: -1px;
margin-top: 1px;
opacity: 0;
}