React结合Drag API实现拖拽示例详解
Drag API
React中的Drag API是用于实现拖放功能的API。该API由React DnD库提供,可用于实现拖放操作,例如将元素从一个位置拖动到另一个位置。
React DnD库提供了两种Drag API:基于HTML5的拖放API和自定义实现的拖放API。
基于HTML5的拖放API是使用浏览器提供的原生拖放功能,该功能可以拖动任何元素,并支持将元素拖动到其他应用程序中。自定义实现的拖放API则提供了更多的灵活性和控制,但需要更多的编码。
要使用Drag API,需要创建一个Drag Source和一个Drop Target。Drag Source是需要拖动的元素,Drop Target是可以放置拖动元素的位置。通过将Drag Source和Drop Target包装在Drag and Drop容器中,可以启用拖放功能。
认识拖拽
鼠标拖拽是一个常见的交互场景,在这个熟悉的过程将会发生哪些事件?
拖拽事件指用户通过鼠标(或其他指针设备)将元素移到一个新的位置上。拖拽过程涉及两个对象:被拖拽元素(上图中 A )和可释放目标(上图中 B )
被拖拽元素
默认情况下,图片、链接和文本是可拖动的。HTML5 在所有 HTML 元素上规定了一个 draggable
属性, 表示元素是否可以拖动。图片和链接的 draggable 属性自动被设置为 true,而其他所有元素此属性的默认值为 false。
某个元素被拖动时,会依次触发以下事件:
ondragstart
:拖动开始,当鼠标按下并且开始移动鼠标时,触发此事件;整个周期只触发一次;ondrag
:只要元素仍被拖拽,就会持续触发此事件;ondragend
:拖拽结束,当鼠标松开后,会触发此事件;整个周期只触发一次。
可释放目标
当把拖拽元素移动到一个有效的放置目标时,目标对象会触发以下事件:
ondragenter
:只要一把拖拽元素移动到目标时,就会触发此事件;ondragover
:拖拽元素在目标中拖动时,会持续触发此事件;ondragleave
或ondrop
:拖拽元素离开目标时(没有在目标上放下),会触发ondragleave
;当拖拽元素在目标放下(松开鼠标),则触发ondrop
事件。
🏝 目标元素默认是不能够被拖放的,即不会触发
ondrop
事件,可以通过在目标元素的ondragover
事件中取消默认事件来解决此问题。
生命周期
拖拽操作中的数据传输
除非数据受影响,否则简单的拖放并没有实际意义。为实现拖动操作中的数据传输,event
对象上暴露了 dataTransfer
对象,用于从被拖动元素向放置目标传递字符串数据。我们使用它来通知画布,当前需要渲染的组件是什么。
dataTransfer 对象主要有两个方法:getData() 和 setData(),分别用来获取和存储值。setData()的第一个参数以及 getData()的唯一参数是一个字符串,表示要设置的数据类型:"text"或"URL"
🏝 虽然这两种数据类型是 IE 最初引入的,但 HTML5 已经将其扩展为允许任何 MIME 类型。为向后 兼容,HTML5 还会继续支持"text"和"URL",但它们会分别被映射到"text/plain"和"text/uri-list”
需要注意的是:存储在 dataTransfer
对象中的数据只能在放置事件中读取。如果没有在 ondrop
事件中取得这些数据,dataTransfer
对象就会被销毁,数据也会丢失。
代码实现
我在项目中使用 React 来实现,并且考虑到跨组件通信,我使用了 dva 来管理数据流。
如何标记当前拖拽的元素?
HTML5 支持的 data-x
属性,我们可以将当前组件的类型 Rectangle 赋值给它,这样处理和画布组件通信方便一些
const Block = (props) => {const handleDragStart = (e: React.DragEvent<HTMLDivElement>) => {// 向拖拽数据中添加项目 e.dataTransfer.setData('text', e.target.dataset.index);};return (<div onDragStart={handleDragStart}><Button draggable data-index="Rectangle">二维码</Button></div>);
};
在上文中讲到,dataTransfer 的数据必须在 handleDrop 方法中获取。实际的用来保存画布中的所有组件的数据:
function DragEditor(props) {const { dvaStore, dispatch } = props;// 阻止浏览器默认事件,否则 ondrop 不会触发const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {e.preventDefault();};const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {e.preventDefault();// 获取拖拽元素的组件类型const type = e.dataTransfer.getData('text');// COMPONENT_LIST 定义了组件的数据格式,根据 type 匹配const component = COMPONENT_LIST.filter((i) => i.component === type,)[0];// 将组件数据添加到 store,画布将会根据数据渲染出组件if (component) {dispatch?.({type: 'store/addComponent',payload: component,});}};return (...);
}
在画布中拖动
拖动主要依赖组件的初始位置,鼠标开始位置、结束位置。根据后两组得到鼠标移动的距离,和初始位置相加后,得到最终位置。
function DragEditor(props: IEditorProps) {const { dvaStore, dispatch } = props;const [startAxis, setStartAxis] = React.useState({ x: 0, y: 0 }); // 鼠标开始拖动时的位置const handleDragStart = (e: React.DragEvent<HTMLDivElement>) => {setStartAxis({ x: e.clientX, y: e.clientY });};const handleDragEnd = (e: React.DragEvent<HTMLDivElement>, data: IComponentSchema) => {// 鼠标移动的距离const displacementX = e.clientX - startAxis.x;const displacementY = e.clientY - startAxis.y;// 计算组件的终点位置:初始位置 + 鼠标移动的距离const endX = Number(data.style.left) + displacementX;const endY = Number(data.style.top) + displacementY;// 限制坐标的最小值为 0const top = Math.max(endY, 0);const left = Math.max(endX, 0);// 更新当前组件样式dispatch?.({type: 'store/setShapeStyle',payload: { top, left },});};return ({dvaStore.componentsData.map((i) => {return (<RenderComponenttype={i.component}componentData={i}key={i.generateId}onDragStart={handleDragStart}onDragEnd={(e) => handleDragEnd(e, i)}/>);})});
}
数据结构
最后,就是组件和数据结构的设计,RenderComponent 是一个自定义的组件,会根据传入的 type 属性渲染对应的组件。组件的数据结构设计如下:
export const COMPONENT_LIST = [{component: 'Rectangle', // 组件名称label: '矩形', // 左侧 Blocks 组件列表中显示的名字propValue: '', // 组件所使用的值icon: 'BorderOuterOutlined', // 左侧组件列表中显示的 icon 图标animations: [], // 动画列表events: {}, // 事件列表style: { // 组件样式width: 100,height: 100,top: 0,left: 0,},},{component: 'Text',label: '文字',propValue: '文字',icon: '',animations: [],events: {},style: {width: 200,height: 33,fontSize: 14,fontWeight: 500,lineHeight: '',letterSpacing: 0,textAlign: '',color: '',},},
];
总结
拖拽是非常有趣的一种交互,特别是在低代码场景下非常重要。使用原生 API 能够让我们更加了解底层的一些细节,React 社区也有一些优秀的第三方框架,如:react-dragable, react-beautiful-dnd,大家有兴趣不妨再多了解下。
相关文章:
React结合Drag API实现拖拽示例详解
Drag API React中的Drag API是用于实现拖放功能的API。该API由React DnD库提供,可用于实现拖放操作,例如将元素从一个位置拖动到另一个位置。 React DnD库提供了两种Drag API:基于HTML5的拖放API和自定义实现的拖放API。 基于HTML5的拖放AP…...
【华为OD机试java、python、c++、jsNode】新学校选址(100%通过+复盘思路)
代码请进行一定修改后使用,本代码保证100%通过率。本文章提供java、python、c++、jsNode四种代码。复盘思路在文章的最后 题目描述 为了解新学期学生暴涨的问题,小乐村要建立所新学校, 考虑到学生上学安全问题,需要所有学生家到学校的距离最短。 假设学校和所有学生家都走在…...

Nacos配置中心,分组配置参考,以及python、go、bash客户端连接获取
Nacos使用说明 nacos官方网站 https://nacos.io/zh-cn/docs/v2/what-is-nacos.html 1、基本配置说明 nacosIP地址:http://xxxxx:8848/nacos/ 服务管理端登录账号:nacos XXX Java最小配置,其他客户端可参考,配置可对应到第三章…...
node-red中有关用户登录,鉴权,权限控制的流程解析
前言 默认地,node-red编辑器可以被任何访问的用户操作,包括修改节点,流数据,重新部署流。 这种默认的部署方式只适用于运行在可靠的网络中。下面我就给大家介绍一下,在公网上部署node-red后,如何对其进行安全加固和权限验证。 主要分为三部分 开启https权限保护编辑器和…...

MQTT协议-使用CONNECT报文连接阿里云
使用网络调试助手发送CONNECT报文连接阿里云 参考:https://blog.csdn.net/daniaoxp/article/details/103039296 在前面文章介绍了如何组装CONNECT报文,以及如何计算剩余长度 CONNECT报文:https://blog.csdn.net/weixin_46251230/article/d…...

每日学术速递3.8
CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Unleashing Text-to-Image Diffusion Models for Visual Perception 标题:释放用于视觉感知的文本到图像扩散模型 作者:Wenliang Zhao, Yongming Rao, Zuya…...
测牛学堂:软件测试之接口测试理论基础总结
接口概念 接口:系统之间数据交互的通道。 这个系统,可以是外部和内部,也可以是两个内部系统之间的通道。 比如我们前端的登录信息,主要是用户名和密码,它通过接口传递给后端,后端校验以后,把结…...

基于土壤数据与机器学习算法的农作物推荐算法代码实现
1.摘要 近年来,机器学习方法在农业领域的应用取得巨大成功,广泛应用于科 学施肥、产量预测和经济效益预估等领域。根据土壤信息进行数据挖掘,并在此基础上提出区域性作物的种植建议,不仅可以促进农作物生长从而带来经济效益&#…...
python中html必备基础知识
<!DOCTYPE html>此标签表示这是一个html文件<heml lang"en">向搜索引擎表示该页面是html语言,并且语言为英文网站,其"lang"的意思就是“language”,语言的意思,而“en”即表示English<head>…...

【专项训练】前言:刻意练习,不断的过遍数才是王道
如何精通一个领域? 拆分知识点刻意练习:每个区域的基础动作分解训练和反复刻意练习反馈(主动反馈、被动反馈、及时反馈)任何知识体系都是一颗树,一定要梳理成思维导图,明确知识与知识之间的关系! 通过7-8周密集训练,练好基本功,彻底攻克LeetCode! 严格执行五毒神掌!…...

【Leetcode】反转链表 合并链表 相交链表 链表的回文结构
目录 一.【Leetcode206】反转链表 1.链接 2.题目再现 3.解法A:三指针法 二.【Leetcode21】合并两个有序链表 1.链接 2.题目再现 3.三指针尾插法 三.【Leetcode160】相交链表 1.链接 2.题目再现 3.解法 四.链表的回文结构 1.链接 2.题目再现 3.解法 一.…...

M1、M2芯片Mac安装虚拟机
目录前言一、安装二、网络设置三、连接SSH客户端前言 一直想着给M1 Mac上安装虚拟机,奈何PD收费,找的破解也不稳定,安装上镜像就起不来。 注:挂长久的分享莫名其妙被封,需要安装包请私信我。 一、安装 虚拟机选择&a…...

算法刷题-只出现一次的数字、输出每天是应该学习还是休息还是锻炼、将有序数组转换为二叉搜索树
只出现一次的数字(位运算、数组) 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 说明: 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗&…...
详解专利对学生、老师和企业员工、创业者、积分落户、地方补助的好处
大家好,我是英子老师。作为一名知识产权专家,深耕于专利行业十余年,具有丰富的专利工作经验:曾在大型专利代理机构从事专利代理工作、专利质检工作(抽查代理机构的专利代理人的撰写质量并评分);之后在知名上市企业、行业龙头企业担任高级专利工程师的职位,主要工作内容…...

Python图像处理:频域滤波降噪和图像增强
图像处理已经成为我们日常生活中不可或缺的一部分,涉及到社交媒体和医学成像等各个领域。通过数码相机或卫星照片和医学扫描等其他来源获得的图像可能需要预处理以消除或增强噪声。频域滤波是一种可行的解决方案,它可以在增强图像锐化的同时消除噪声。 …...
智能手机高端“酣战”,转机在何方?
经过多年发展,如今全世界有七成手机由中国制造,但在利润最丰厚的高端市场,国产厂商在很长一段时间之内都是形单影只,曾经一度跻身高端的“华为”因为封禁成了“绝唱”。 华为“失声”高端之后,其他一众国产厂商或主动…...

K8s pod 动态弹性扩缩容 HPA
一、概述Horizontal Pod Autoscaler(HPA,Pod水平自动伸缩),根据平均 CPU 利用率、平均内存利用率或你指定的任何其他自定义指标自动调整 Deployment 、ReplicaSet 或 StatefulSet 或其他类似资源,实现部署的自动扩展和…...
C++中的类简要介绍
文章目录前言一、什么是类什么是对象1.类的概述2.对象的概述二、如何创建使用类三、class和struct创建类时的区别1.访问级别2.继承方式总结前言 本篇文章讲给大家介绍一个C中重要的概念,了解了这个概念大家就明白了为什么C会叫做面向对象编程了。 一、什么是类什么…...

项目管理工具DHTMLX Gantt灯箱元素配置教程:只读模式
DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的大部分开发需求,具备完善的甘特图图表库,功能强大,价格便宜,提供丰富而灵活的JavaScript API接口,与各种服务器端技术&am…...

从LiveData迁移到Kotlin的 Flow,才发现是真的香!
LiveData 对于 Java 开发者、初学者或是一些简单场景而言仍是可行的解决方案。而对于一些其他的场景,更好的选择是使用 Kotlin 数据流 (Kotlin Flow)。虽说数据流 (相较 LiveData) 有更陡峭的学习曲线,但由于它是 JetBrains 力挺的 Kotlin 语言的一部分&…...

网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...

【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...