moveable一个可拖拽、缩放、旋转、对齐的库

8,479 阅读2分钟

啥也不说 先上效果图

动画.gif

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 &amp;&amp; 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 &amp;&amp; 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等,可以根据文档来一一实现