啥也不说 先上效果图
moveable是什么
moveable是一个可拖动的、可调整大小的、可缩放的、可旋转的、可弯曲的、可收缩的、可分组的、可对齐的一个插件,包含了不同框架的版本 这边技术栈是React,直接在React中将需要拖动的元素绑定到moveable上即可实现,对该元素的拖动,调整大小,旋转,对齐等操作
实现步骤
*注意点
react和react-dom版本不能超过18,我这边是用的17
1.下载依赖
npm i react-moveable
2.使用moveable
拖拽
export default function MoveItem () {
const [target, setTarget] = useState(null)
const [frame, setFrame] = useState({
translate: [0,0],
});
useEffect(() => {
setTarget(document.querySelector('.target'))
}, [])
// 拖拽
function handleDragStart (e) {
e.set(frame.translate);
}
function handleDrag (e) {
frame.translate = e.beforeTranslate;
e.target.style.transform = `translate(${e.beforeTranslate[0]}px, ${e.beforeTranslate[1]}px)`;
}
return (
<div className='main'>
<div className='target'>
</div>
<Moveable
target={target} // moveable的对象
draggable // 是否可以拖拽
padding={{"left":0,"top":0,"right":0,"bottom":0}} // padding距离
zoom={1} // 缩放包裹的moveable
origin={true} // 显示中心点
throttleDrag={0} // 拖拽阈值 达到这个值才执行拖拽
onDragStart={handleDragStart} // 拖动开始执行
onDrag={handleDrag} // 拖动中
/>
</div>
)
}
拖拽设置边界
export default function MoveItem () {
const [target, setTarget] = useState(null)
const [frame, setFrame] = useState({
translate: [0,0],
});
let [snapContainer, setSnapContainer] = useState(null)
let [dropBounds, setDropBounds] = useState([])
useEffect(() => {
setTarget(document.querySelector('.target'))
setSnapContainer(document.querySelector('.main'))
let dom = document.querySelector('.center-drop')
// getBoundingClientRect()获取元素到窗口的距离
dropBounds = dom.getBoundingClientRect()
setDropBounds({
top: 0,
left: 0,
right: dropBounds.width,
bottom: dropBounds.height,
})
}, [])
// 拖拽
function handleDragStart (e) {
e.set(frame.translate);
}
function handleDrag (e) {
frame.translate = e.beforeTranslate;
e.target.style.transform = `translate(${e.beforeTranslate[0]}px, ${e.beforeTranslate[1]}px)`;
}
return (
<div className='main'>
<div className='target'>
</div>
<Moveable
target={target} // moveable的对象
draggable // 是否可以拖拽
padding={{"left":0,"top":0,"right":0,"bottom":0}} // padding距离
zoom={1} // 缩放包裹的moveable
origin={true} // 显示中心点
throttleDrag={0} // 拖拽阈值 达到这个值才执行拖拽
onDragStart={handleDragStart} // 拖动开始执行
onDrag={handleDrag} // 拖动中
snappable={true} // 是否初始化磁吸功能
snapContainer={snapContainer} // 磁吸功能的容器
bounds={dropBounds} // 边界点
/>
</div>
)
}
拖拽+缩放
export default function MoveItem () {
const [target, setTarget] = useState(null)
const [frame, setFrame] = useState({
translate: [0,0],
});
let [snapContainer, setSnapContainer] = useState(null)
let [dropBounds, setDropBounds] = useState([])
useEffect(() => {
setTarget(document.querySelector('.target'))
setSnapContainer(document.querySelector('.main'))
let dom = document.querySelector('.center-drop')
// getBoundingClientRect()获取元素到窗口的距离
dropBounds = dom.getBoundingClientRect()
setDropBounds({
top: 0,
left: 0,
right: dropBounds.width,
bottom: dropBounds.height,
})
}, [])
// 拖拽
function handleDragStart (e) {
e.set(frame.translate);
}
function handleDrag (e) {
frame.translate = e.beforeTranslate;
e.target.style.transform = `translate(${e.beforeTranslate[0]}px, ${e.beforeTranslate[1]}px)`;
}
// 缩放
function handleResizeStart (e) {
e.setOrigin(["%", "%"]);
e.dragStart && e.dragStart.set(frame.translate);
}
function handleResize (e) {
const beforeTranslate = e.drag.beforeTranslate;
frame.translate = beforeTranslate;
e.target.style.width = `${e.width}px`;
e.target.style.height = `${e.height}px`;
e.target.style.transform = `translate(${beforeTranslate[0]}px, ${beforeTranslate[1]}px)`;
}
return (
<div className='main'>
<div className='target'>
</div>
<Moveable
target={target} // moveable的对象
draggable // 是否可以拖拽
padding={{"left":0,"top":0,"right":0,"bottom":0}} // padding距离
zoom={.6} // 缩放包裹的moveable
origin={true} // 显示中心点
throttleDrag={0} // 拖拽阈值 达到这个值才执行拖拽
onDragStart={handleDragStart} // 拖动开始执行
onDrag={handleDrag} // 拖动中
snappable={true} // 是否初始化磁吸功能
snapContainer={snapContainer} // 磁吸功能的容器
bounds={dropBounds} // 边界点
resizable // 是否可以缩放
edgeDraggable // 是否通过拖动边缘线移动
throttleResize={0} // 缩放阈值
renderDirections={["nw","n","ne","w","e","sw","s","se"]} // 变化的点
edge //resize,scale是否支持通过边框操作
onResizeStart={handleResizeStart} // 缩放开始时
onResize={handleResize} // 缩放中
/>
</div>
)
}
拖拽+缩放+旋转
旋转和拖拽都是设置的transfrom属性来实现的,旋转和拖拽同时存在时旋转会把拖拽覆盖,拖拽会把旋转覆盖,需要一起设置才能一起生效
transform: translate(10px,10px) rotate(45deg)。
export default function MoveItem () {
const [target, setTarget] = useState(null)
const [frame, setFrame] = useState({
translate: [0,0],
rotate: 0
});
let [snapContainer, setSnapContainer] = useState(null)
let [dropBounds, setDropBounds] = useState([])
useEffect(() => {
setTarget(document.querySelector('.target'))
setSnapContainer(document.querySelector('.main'))
let dom = document.querySelector('.center-drop')
// getBoundingClientRect()获取元素到窗口的距离
dropBounds = dom.getBoundingClientRect()
setDropBounds({
top: 0,
left: 0,
right: dropBounds.width,
bottom: dropBounds.height,
})
}, [])
// 拖拽
function handleDragStart (e) {
e.set(frame.translate);
}
function handleDrag (e) {
frame.translate = e.beforeTranslate;
e.target.style.transform = `translate(${e.beforeTranslate[0]}px, ${e.beforeTranslate[1]}px) rotate(${frame.rotate}deg)`;
}
// 缩放
function handleResizeStart (e) {
e.setOrigin(["%", "%"]);
e.dragStart && e.dragStart.set(frame.translate);
}
function handleResize (e) {
const beforeTranslate = e.drag.beforeTranslate;
frame.translate = beforeTranslate;
e.target.style.width = `${e.width}px`;
e.target.style.height = `${e.height}px`;
e.target.style.transform = `translate(${beforeTranslate[0]}px, ${beforeTranslate[1]}px)`;
}
// 旋转
function handleRotateStart (e) {
e.set(frame.rotate);
}
function handleRotate (e) {
frame.rotate = e.beforeRotate;
target.style.transform = `translate(${frame.translate[0]}px, ${frame.translate[1]}px) rotate(${e.beforeRotate}deg)`
}
return (
<div className='main'>
<div className='target'>
</div>
<Moveable
target={target} // moveable的对象
draggable // 是否可以拖拽
padding={{"left":0,"top":0,"right":0,"bottom":0}} // padding距离
zoom={.6} // 缩放包裹的moveable
origin={true} // 显示中心点
throttleDrag={0} // 拖拽阈值 达到这个值才执行拖拽
onDragStart={handleDragStart} // 拖动开始执行
onDrag={handleDrag} // 拖动中
snappable={true} // 是否初始化磁吸功能
snapContainer={snapContainer} // 磁吸功能的容器
bounds={dropBounds} // 边界点
resizable // 是否可以缩放
edgeDraggable // 是否通过拖动边缘线移动
throttleResize={0} // 缩放阈值
renderDirections={["nw","n","ne","w","e","sw","s","se"]} // 变化的点
edge //resize,scale是否支持通过边框操作
onResizeStart={handleResizeStart} // 缩放开始时
onResize={handleResize} // 缩放中
rotatable // 旋转
throttleRotate={0} // 旋转阈值
rotationPosition={"top"} // 旋转方向
onRotateStart={handleRotateStart} // 旋转开始时
onRotate={handleRotate} // 旋转中
/>
</div>
)
}
拖拽+缩放+旋转+对齐
实现对齐辅助线和停靠需要一个
elementGuidelines来放置需要与之对齐的dom元素列表
return (
<div className='main'>
<div className='target'>
</div>
<div className='guide'>
对齐我
</div>
<div className='guide guide-two'>
对齐我
</div>
<Moveable
target={target} // moveable的对象
draggable // 是否可以拖拽
padding={{"left":0,"top":0,"right":0,"bottom":0}} // padding距离
zoom={.6} // 缩放包裹的moveable
origin={true} // 显示中心点
throttleDrag={0} // 拖拽阈值 达到这个值才执行拖拽
onDragStart={handleDragStart} // 拖动开始执行
onDrag={handleDrag} // 拖动中
snappable={true} // 是否初始化磁吸功能
snapContainer={snapContainer} // 磁吸功能的容器
bounds={dropBounds} // 边界点
resizable // 是否可以缩放
edgeDraggable // 是否通过拖动边缘线移动
throttleResize={0} // 缩放阈值
renderDirections={["nw","n","ne","w","e","sw","s","se"]} // 变化的点
edge //resize,scale是否支持通过边框操作
onResizeStart={handleResizeStart} // 缩放开始时
onResize={handleResize} // 缩放中
rotatable // 旋转
throttleRotate={0} // 旋转阈值
rotationPosition={"top"} // 旋转方向
onRotateStart={handleRotateStart} // 旋转开始时
onRotate={handleRotate} // 旋转中
elementGuidelines={elementGuidelines} // 磁吸效果辅助线的dom列表
snapGap={true} // 开启辅助线
snapDirections={{"top":true,"right":true,"bottom":true,"left":true,"center": true, "middle": true}} // 辅助线方向
elementSnapDirections={{"top":true,"right":true,"bottom":true,"left":true,"center": true, "middle": true}} // 元素捕捉方向
snapThreshold={5} // 辅助线阈值 ,即元素与辅助线间距小于x,则自动贴边
isDisplaySnapDigit={true} // 是否展示与磁吸辅助线的距离
snapDigit={0} //捕捉距离数字
/>
</div>
)
标线
如果需要在整个容器加标线对齐,设置
verticalGuidelines={[0,200,400]}
horizontalGuidelines={[0,200,400]}
演示项目的gitee地址,如有帮助,欢迎点赞。
moveable还有其他很多功能,如Groupable、Warpable、selectto等,可以根据文档来一一实现