react可视化编辑器 第二章 自由拖动

完整代码

这里介绍 currentDiv 和 useRef的俩中用法,看自己需求使用

import React, {
  useState,
  DragEvent,
  useRef,
  useEffect,
  MouseEvent,
} from 'react';

interface Demo {
  id: number;
  x: number;
  y: number;
}

const App: React.FC = () => {
  const [demos, setDemos] = useState<Demo[]>([]);
  // let currentDiv: HTMLDivElement | null = null;
  const divRef = useRef<HTMLDivElement | null>(null);

  const handleDragStart = (e: DragEvent<HTMLDivElement>, id: number) => {
    e.dataTransfer.setData('text/plain', id.toString());

    const offsetX = e.clientX - e.currentTarget.getBoundingClientRect().left;
    const offsetY = e.clientY - e.currentTarget.getBoundingClientRect().top;
    e.dataTransfer.setData('offsetX', offsetX.toString());
    e.dataTransfer.setData('offsetY', offsetY.toString());
  };

  const handleDrop = (e: DragEvent<HTMLDivElement>) => {
    e.preventDefault();

    const clientX = e.clientX;
    const clientY = e.clientY;
    const contentStyle = document
      .getElementById('content')
      .getBoundingClientRect();
    const offsetX = e.dataTransfer.getData('offsetX');
    const offsetY = e.dataTransfer.getData('offsetY');
    const x = clientX - contentStyle.left - offsetX;
    const y = clientY - contentStyle.top - offsetY;
    const newDemo: Demo = { x, y, id: +new Date() };
    setDemos([...demos, newDemo]);
  };

  const handleDragOver = (e: DragEvent<HTMLDivElement>) => {
    e.preventDefault();
  };

  // const onMouseDown = (e: MouseEvent<HTMLDivElement>) => {
  //   console.info('onMouseDown', e);
  // };

  // const onMouseUp = (e: MouseEvent<HTMLDivElement>) => {
  //   console.info('onMouseUp', e);
  // };

  const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
    e.stopPropagation();

    // currentDiv = e.currentTarget;
    divRef.current = e.currentTarget;

    // let { top, left } = currentDiv.style;
    // console.info('top, left', top, left);
    let { top, left } = divRef.current.style;
    console.info('top, left', top, left);

    // 如果直接修改属性,值的类型会变为字符串,所以要转为数值型
    const startTop = top ? Number(top.replace('px', '')) : 0;
    const startLeft = left ? Number(left.replace('px', '')) : 0;

    const startY = e.clientY;
    const startX = e.clientX;

    const move = (moveEvent: { clientX: number; clientY: number }) => {
      // if (!currentDiv) return; // 检查currentDiv是否存在
      if (!divRef.current) return; // 检查currentDiv是否存在

      const currX = moveEvent.clientX;
      const currY = moveEvent.clientY;
      console.info('move', currX, currY);

      top = `${currY - startY + startTop}px`;
      left = `${currX - startX + startLeft}px`;

      // 修改当前组件样式
      // currentDiv.style.left = left;
      // currentDiv.style.top = top;

      // 修改当前组件样式
      divRef.current.style.left = left;
      divRef.current.style.top = top;
    };

    const up = () => {
      document.removeEventListener('mousemove', move);
      document.removeEventListener('mouseup', up);
      // currentDiv = null; // 清除对元素的引用
      divRef.current = null;
      console.log('removeEventListener');
    };

    document.addEventListener('mousemove', move);
    document.addEventListener('mouseup', up);
  };

  return (
    <div>
      <div
        id="demo"
        draggable
        onDragStart={(e) => handleDragStart(e, 1)}
        style={{
          width: '100px',
          height: '100px',
          backgroundColor: 'red',
          margin: '30px',
          cursor: 'pointer',
        }}
      >
        demo2
      </div>
      <div
        id="content"
        onDrop={handleDrop}
        onDragOver={handleDragOver}
        style={{
          width: '300px',
          height: '300px',
          margin: '30px',
          backgroundColor: 'blue',
          position: 'relative',
        }}
      >
        content
        {demos.map((demo) => (
          <div
            onMouseDown={handleMouseDown}
            key={demo.id}
            style={{
              width: '100px',
              height: '100px',
              backgroundColor: 'red',
              cursor: 'pointer',
              position: 'absolute',
              left: `${demo.x}px`,
              top: `${demo.y}px`,
            }}
          >
            demo {demo.id}
          </div>
        ))}
      </div>
    </div>
  );
};

export default App;
相关推荐
我要洋人死12 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人24 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人24 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR30 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香32 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969335 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai40 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
真·Wild·攻城狮1 小时前
【码农日常】Vscode Clangd初始化失败(Win10)
ide·vscode·编辑器
七灵微1 小时前
【测试】【Debug】vscode中同一个测试用例出现重复
ide·vscode·编辑器
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js