react可视化编辑器 第一章 拖拽

效果:

实现可视化编辑器,第一步难点 是 拖拽

提示:链接和图片默认是可拖动的,不需要draggable属性。

在拖放操作的不同阶段使用并可能发生许多事件属性:

  • 在可拖动目标上触发的事件(源元素):

    ondragstart - 当用户开始拖动元素时触发

    ondrag - 拖动元素时触发

    ondragend - 在用户完成拖动元素时触发

  • 在放置目标上触发的事件:

    ondragenter - 当被拖动的元素进入放置目标时触发

    ondragover - 当被拖动的元素超过放置目标时触发

    ondragleave - 当被拖动的元素离开放置目标时触发

    ondrop - 当被拖动的元素放在放置目标上时触发

这种代码结构更加清楚, demo 和content是俩个完全没有关系的兄弟div

现在需求的 红色拖拽到蓝色中, 这里的方法是定位

情况一:

  • demo的操作逻辑代码

    复制代码
        <div
          id="demo"
          draggable
          onDragStart={(e) => handleDragStart(e, 1)}
          style={{
            width: '100px',
            height: '100px',
            backgroundColor: 'red',
            margin: '30px',
          }}
        >
          demo2
        </div>
    
    const handleDragStart = (e: DragEvent<HTMLDivElement>, id: number) => {
      e.dataTransfer.setData('text/plain', id.toString());  // 存储id, 和 data-XX一个道理
    };
  • content 的逻辑代码

    复制代码
     <div
          id="content"
          onDrop={handleDrop}
          style={{
            width: '300px',
            height: '300px',
            margin: '30px',
            backgroundColor: 'blue',
            position: 'relative',
          }}
        >
          content
          {demos.map((demo) => (
            <div
              key={demo.id}
              style={{
                width: '100px',
                height: '100px',
                backgroundColor: 'red',
                position: 'absolute',
                left: `${demo.x}px`,
                top: `${demo.y}px`,
              }}
            >
              demo {demo.id}
            </div>
          ))}
        </div>
    
    const handleDrop = (e: DragEvent<HTMLDivElement>) => {
      e.preventDefault();
      const clientX = e.clientX;
      const clientY = e.clientY;
      const contentStyle = document
        .getElementById('content')
        .getBoundingClientRect();
    
      const x = clientX - contentStyle.left
      const y = clientY - contentStyle.top;
      const newDemo: Demo = { x, y, id: +new Date() };
      setDemos([...demos, newDemo]);
    };

上面代码测试结果:

代码测试 ,会有一些偏差,原因是 鼠标拖拽的位置的不是红色div的左上角顶点, 这样的就不会发生偏移, 但是实际情况无法保证每次都是拖拽顶点, 那需要在开始拖拽的计算的鼠标相对于红色div的偏移值

情况二:完整的代码

复制代码
import React, { useState, DragEvent, useEffect, MouseEvent } from 'react';

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

const App: React.FC = () => {
  const [demos, setDemos] = useState<Demo[]>([]);

  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 onDragEnd = (e: MouseEvent<HTMLDivElement>) => {
    console.info('onDragEnd', e);
  };

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

export default App;
相关推荐
摇滚侠2 小时前
npm 设置了阿里云镜像,然后全局安装了 pnpm,pnpm 还需要设置阿里云镜像吗
前端·阿里云·npm
CaracalTiger2 小时前
什么是Clawdbot?Clawdbot下载、安装、配置教程(最新版Moltbot)
python·编辑器·aigc·idea·ai编程·intellij idea·agi
程序员清洒8 小时前
Flutter for OpenHarmony:GridView — 网格布局实现
android·前端·学习·flutter·华为
VX:Fegn08958 小时前
计算机毕业设计|基于ssm + vue超市管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
0思必得08 小时前
[Web自动化] 反爬虫
前端·爬虫·python·selenium·自动化
LawrenceLan8 小时前
Flutter 零基础入门(二十六):StatefulWidget 与状态更新 setState
开发语言·前端·flutter·dart
秋秋小事8 小时前
TypeScript 模版字面量与类型操作
前端·typescript
2401_892000529 小时前
Flutter for OpenHarmony 猫咪管家App实战 - 添加提醒实现
前端·javascript·flutter
Yolanda949 小时前
【项目经验】vue h5移动端禁止缩放
前端·javascript·vue.js
广州华水科技10 小时前
单北斗GNSS形变监测一体机在基础设施安全中的应用与技术优势
前端