react 打造页面可视化搭建 4 -实现拖拽自定义容器大小 持续更新

可视化搭建的项目要求元素能够像作图软件同样随意的拖动改变大小 那怎么实现这个功能呢?先来一个简单的版本吧?javascript

点击进入github的demo地址,欢迎star github.com/songxuecc/R…java

基础js事件

  1. mouseDown : 鼠标的键钮被按下
  2. mousemove : 鼠标在目标的上方移动
  3. mouseup : 鼠标的键钮被释放弹起
  4. mouseleave : 鼠标移出元素范围时触发

原理

  1. 鼠标在盒子所在区域按下时获取移动起始位置 并 绑定mousemove mouseup mouseleave事件监听
  2. 在滑动过程当中算出移动的距离加上元素宽高即元素最终宽高
  3. 在鼠标释放后解绑事件

简单实现的效果及代码

const SimpleDemo = () => {
  const [style, setState] = React.useState({ width: 250, height: 120 });
  const origin = React.useRef(null);

// 鼠标移动 计算宽高并更新元素style
  const onMouseMove = event => {
    event.stopPropagation();
    event.preventDefault();
    const clientX = event.clientX;
    const clientY = event.clientY;
    const width = style.width + clientX - origin.current.x;
    const height = style.height + clientY - origin.current.y;
    setState({ width, height });
  };
  
  // 鼠标按下 
  const onMouseDown = event => {
    event.stopPropagation();
    event.preventDefault();
    const clientX = event.clientX;
    const clientY = event.clientY;
    // origin移动起始坐标轴
    origin.current = { x: clientX, y: clientY };
    bindEvents();
  };

    // 鼠标释放后解绑事件   
  const onMouseUp = event => {
    unbindEvents();
  };

  const bindEvents = () => {
    document.addEventListener("mouseup", onMouseUp);
    document.addEventListener("mousemove", onMouseMove);
    document.addEventListener("mouseleave", onMouseUp);
  };
  const unbindEvents = () => {
    document.removeEventListener("mouseup", onMouseUp);
    document.removeEventListener("mousemove", onMouseMove);
    document.removeEventListener("mouseleave", onMouseUp);
  };

  React.useEffect(() => {
    return () => {
      unbindEvents();
    };
  }, []);

  return (
    <div style={{ border: "1px solid green" }}>
      我是绿色的盒子的盒子
      <div
        onMouseDown={onMouseDown}
        style={{ border: "1px solid red", ...style }}
      >
        <h3>我是红色的盒子,点我拖动改变大小</h3>
      </div>
    </div>
  );
};
复制代码

像作图软件同样随意的拖动改变大小

效果

原理

  1. 锁定六个方向的点point ,给每一个ponit绑定onMouseDown
const [pointList] = React.useState([
    "lt",
    "rt",
    "lb",
    "rb",
    "l",
    "r",
    "t",
    "b"
  ]);
复制代码
  1. 点击ponit的时候,开始绑定事件后续就和上面的差很少了
<div>
    <div>我是要改变大小的元素A</div>
    <div>
        经过拖动我能够改变元素A的大小
        <Point>
    </div>
<div>
复制代码

代码较多 能够直接fork个人仓库 连接在文章开头 下文为主要代码
或者在线演示 codesandbox.io/s/zen-bogda…git

const onMouseMove = moveEvent => {
    const point = ref.current.pointId;
    let downEvent = moveEvent;
    downEvent.stopPropagation();
    downEvent.preventDefault();
    const activeNode = document.getElementById("box");
    const activeStyle = props.style;
    if (!activeNode) {
      return;
    }
    const { pos, startX, startY } = ref.current;
    const nextPos = { ...pos };
    ref.current.hasMoved = true;
    let height = Number(nextPos.height);
    let width = Number(nextPos.width);
    let top = Number(nextPos.top) || 0;
    let left = Number(nextPos.left) || 0;
    let currX = moveEvent.clientX;
    let currY = moveEvent.clientY;
    let disY = currY - startY;
    let disX = currX - startX;
    let hasT = /t/.test(point);
    let hasB = /b/.test(point);
    let hasL = /l/.test(point);
    let hasR = /r/.test(point);
    let newHeight = +height + (hasT ? -disY : hasB ? disY : 0);
    let newWidth = +width + (hasL ? -disX : hasR ? disX : 0);
    nextPos.height = newHeight > 0 ? newHeight : 0;
    nextPos.width = newWidth > 0 ? newWidth : 0;
    nextPos.left = +left + (hasL ? disX : 0);
    nextPos.top = +top + (hasT ? disY : 0);
    // 根据拖动方向算出最终拖动结果style 并在拖动过程当中保持cursor 不变
    // 画布和被拖动元素的cursor 在拖动时候与Point保持一致
    const style = {
      ...activeStyle,
      left: `${nextPos.left}px`,
      top: `${nextPos.top}px`,
      width: `${nextPos.width}px`,
      height: `${nextPos.height}px`,
      cursor: directionKey[point]
    };
    const paiting = document.getElementById("painting-main");
    if (paiting) {
      paiting.style.cursor = directionKey[point];
    }
    stateRef.current = style;
    props.handleStyle(stateRef.current);
  };
  
  const onMouseUp = event => {
    // 记录这次操做结果
    if (ref.current.hasMoved) {
    }
    // 还原全部数据
    const paiting = document.getElementById("painting-main");
    if (paiting) {
      paiting.style.cursor = "default";
    }
    stateRef.current = _.omit(stateRef.current, ["cursor"]);
    props.handleStyle(stateRef.current);
    stateRef.current = {};
    ref.current = {};
    // 解除绑定
    unbindEvents();
  };
  
复制代码

但愿各位小伙伴给我点个赞 谢谢啦

相关文章
相关标签/搜索