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;
相关推荐
开开心心就好5 小时前
音频降噪剪辑工具,音量裁剪一键处理
运维·服务器·前端·spring·ocr·音视频·1024程序员节
PedroQue997 小时前
0.8.2 新增 backFallback 配置,无 tabbar 页返回无忧
前端·uni-app
淡漠的蓝精灵7 小时前
web.py:一个简单到极致的 Python Web 框架
开发语言·前端·python·其他
开开心心就好8 小时前
竖式除法出题工具,整除余数混合模式
运维·服务器·前端·javascript·ocr·perl·1024程序员节
起个名字费劲死了8 小时前
Halcon学习一:骰子识别
前端·python·学习
大江东去浪淘尽千古风流人物8 小时前
【HexVIO】DSP卸载双目惯性VIO:Hexagon向量化前端与单帧融合调度深度解析
前端·状态模式·slam·dsp·vio·hexagon·移动端加速
其实防守也摸鱼8 小时前
Chrome 扩展安全攻防实录:从攻击面到防御实践
开发语言·前端·网络·chrome·安全·web安全·php
web打印社区8 小时前
Lodop排障总纲-常见报错一张表
开发语言·前端·javascript·chrome·websocket
李剑一8 小时前
为什么几乎所有的知名开源项目都是外国人写的?
前端·ai编程·deepseek
小郎君6588 小时前
(一)为什么表单需要第三种状态:disabled、readonly 与纯文本展示
前端·vue.js