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();
  };
  
複製代碼

但願各位小夥伴給我點個贊 謝謝啦

相關文章
相關標籤/搜索