react可视化编辑器 第四章 顶点的缩放功能

直接上代码

import React, { useState, useEffect, useRef, useCallback } from 'react';
import styles from './index.module.scss';

const ResizableDiv = () => {
  // 8个点,为 left/right/top/bottom 的组合值
  const points = ['lt', 'tc', 'rt', 'rc', 'br', 'bc', 'bl', 'lc'];
  const isDown = useRef(false);
  const resizeItemRef = useRef(null);
  const [startPos, setStartPos] = useState({
    startX: 0,
    startY: 0,
    width: 0,
    height: 0,
    direction: '',
  });

  useEffect(() => {
    document.addEventListener('mouseup', handleMouseUp);
    document.addEventListener('mousemove', handleMouseMove);

    return () => {
      document.removeEventListener('mouseup', handleMouseUp);
      document.removeEventListener('mousemove', handleMouseMove);
    };
  }, [isDown, startPos]);

  const handleMouseUp = () => {
    isDown.current = false;
  };

  const handleMouseMove = (e: { clientX: number; clientY: number }) => {
    if (isDown.current && resizeItemRef.current) {
      const { direction } = startPos;
      let { height, width, startX, startY } = startPos;

      const offsetX = e.clientX - startX;
      const offsetY = e.clientY - startY;

      switch (direction) {
        case 'rc':
          // 向右拖拽添加宽度
          width += offsetX;
          break;
        case 'lc':
          // 增加宽度、位置同步左移
          width -= offsetX;
          startX += offsetX;
          break;
        case 'bc':
          height += offsetY;
          break;
        case 'tc':
          height -= offsetY;
          startY += offsetY;
          break;
        case 'rt':
          height -= offsetY;
          startY += offsetY;
          width += offsetX;
          break;
        case 'lt':
          height -= offsetY;
          startY += offsetY;
          width -= offsetX;
          startX += offsetX;
          break;
        case 'br':
          height += offsetY;
          width += offsetX;
          break;
        case 'bl':
          height += offsetY;
          width -= offsetX;
          startX += offsetX;
          break;
      }

      resizeItemRef.current.style.width = width + 'px';
      resizeItemRef.current.style.height = height + 'px';
    }
  };

  // 鼠标被按下
  const onMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
    console.info('onMouseDown', e);
    if (!e.currentTarget) return;
    resizeItemRef.current = e.currentTarget;

    if (!resizeItemRef.current) return;

    const { offsetWidth: width, offsetHeight: height } = resizeItemRef.current;

    const direction = e.target.getAttribute('data-key');
    console.log(direction, width, height);
    isDown.current = true;
    setStartPos({
      startX: e.clientX,
      startY: e.clientY,
      width,
      height,
      direction,
    });
  };

  return (
    <div className={styles['resize-content']}>
      <div className={styles['resize-item']} onMouseDown={onMouseDown}>
        {points.map((item, index) => (
          <div
            key={index}
            data-key={item}
            className={[
              styles['resize-control-btn'],
              styles[`resize-control-${item}`],
            ].join(' ')}
          ></div>
        ))}
      </div>
    </div>
  );
};

export default ResizableDiv;

.resize-content {
  width: 500px;
  height: 500px;
  border: 1px dashed red;
  margin: 30px auto;
  position: relative;
}

.resize-item {
  cursor: move;
  width: 100px;
  height: 100px;
  background-color: #ccc;
  position: absolute;
  // top: 200px;
  // left: 200px;
}

$width_height: 6px;

.resize-control-btn {
  position: absolute;
  width: $width_height;
  height: $width_height;
  background: #000;
  user-select: none; // 注意禁止鼠标选中控制点元素,不然拖拽事件可能会因此被中断
}

.resize-control-btn.resize-control-lt {
  cursor: nw-resize;
  top: 0;
  left: 0;
}
.resize-control-btn.resize-control-tc {
  cursor: ns-resize;
  top: 0;
  left: 50%;
  // transform: translateX(-50%);
  margin-left: $width_height / -2;
}
.resize-control-btn.resize-control-rt {
  cursor: ne-resize;
  top: 0;
  right: 0;
}
.resize-control-btn.resize-control-rc {
  cursor: ew-resize;
  top: 50%;
  margin-top: $width_height / -2;
  right: 0;
}
.resize-control-btn.resize-control-br {
  cursor: se-resize;
  bottom: 0;
  right: 0;
}
.resize-control-btn.resize-control-bc {
  cursor: ns-resize;
  bottom: 0;
  left: 50%;
  margin-left: $width_height / -2;
}
.resize-control-btn.resize-control-bl {
  cursor: sw-resize;
  bottom: 0;
  left: 0;
}
.resize-control-btn.resize-control-lc {
  cursor: ew-resize;
  top: 50%;
  margin-top: $width_height / -2;
  left: 0;
}
相关推荐
fishmemory7sec3 分钟前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
fishmemory7sec6 分钟前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
JUNAI_Strive_ving1 小时前
番茄小说逆向爬取
javascript·python
看到请催我学习1 小时前
如何实现两个标签页之间的通信
javascript·css·typescript·node.js·html5
twins35202 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky2 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~2 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
哪 吒2 小时前
华为OD机试 - 几何平均值最大子数(Python/JS/C/C++ 2024 E卷 200分)
javascript·python·华为od
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
Q_w77423 小时前
一个真实可用的登录界面!
javascript·mysql·php·html5·网站登录