DraggableModal 可拖拽模态框组件使用说明

概述

DraggableModal 是一个基于 @dnd-kit/core 实现的可拖拽模态框组件,允许用户通过拖拽标题栏来移动模态框位置。该组件具有智能边界检测功能,确保模态框始终保持在可视区域内。

功能特性

  • 可拖拽移动:支持通过鼠标拖拽移动模态框位置
  • 智能边界检测:防止模态框被拖拽到屏幕可视区域外
  • 响应式适配:根据窗口大小自动调整可拖拽范围
  • 平滑交互:使用 CSS transform 实现流畅的拖拽动画
  • 类型安全:完整的 TypeScript 类型支持

安装依赖

确保项目中已安装以下依赖:

npm install @dnd-kit/core @dnd-kit/utilities
# 或
yarn add @dnd-kit/core @dnd-kit/utilities

基本用法

1. 导入组件

import DraggableModal from './components/DraggableModal';

2. 基础示例

import React, { useState } from 'react';
import { Modal } from 'antd';
import DraggableModal from './components/DraggableModal';const ExampleComponent: React.FC = () => {const [visible, setVisible] = useState(false);return (<><button onClick={() => setVisible(true)}>打开可拖拽模态框</button><Modaltitle="可拖拽的模态框"open={visible}onCancel={() => setVisible(false)}modalRender={(modal) => (<DraggableModal>{modal}</DraggableModal>)}><p>这是一个可以拖拽的模态框内容</p></Modal></>);
};export default ExampleComponent;

3. 与 Antd Modal 结合使用

import React, { useState } from 'react';
import { Modal, Form, Input, Button } from 'antd';
import DraggableModal from '@/pages/StdFormEdit/components/DraggableModal';const FormModal: React.FC = () => {const [visible, setVisible] = useState(false);const [form] = Form.useForm();const handleSubmit = async () => {try {const values = await form.validateFields();console.log('表单数据:', values);setVisible(false);} catch (error) {console.error('表单验证失败:', error);}};return (<><Button type="primary" onClick={() => setVisible(true)}>打开表单模态框</Button><Modaltitle="用户信息编辑"open={visible}onCancel={() => setVisible(false)}footer={[<Button key="cancel" onClick={() => setVisible(false)}>取消</Button>,<Button key="submit" type="primary" onClick={handleSubmit}>确定</Button>]}modalRender={(modal) => (<DraggableModal>{modal}</DraggableModal>)}><table ...></Modal></>);
};export default FormModal;

API 说明

DraggableModal Props

属性类型默认值说明
childrenReactNode-需要包装的模态框内容

组件内部实现细节

DraggableWrapper Props
属性类型说明
topnumber模态框垂直位置偏移量
leftnumber模态框水平位置偏移量
childrenReactNode子组件内容
modalRefRefObject<HTMLDivElement>模态框DOM引用

技术实现

核心特性

  1. 拖拽识别:自动识别具有 modal-header 类名的元素作为拖拽手柄
  2. 边界限制
    • 垂直方向:上边界 -100px,下边界为窗口高度减去模态框高度再减去100px
    • 水平方向:限制在窗口宽度范围内,保持模态框居中对称
  3. 位置计算:使用 CSS transform 属性实现位置变换,性能优异

边界检测算法

// 垂直边界检测
const needRemoveMinHeight = -100; // 上边界
const needRemoveMaxHeight = winH - 100 - modalRef.current.clientHeight; // 下边界// 水平边界检测  
const needRemoveWidth = (winW - modalRef.current.clientWidth) / 2; // 左右对称边界

使用注意事项

1. 模态框结构要求

确保被包装的模态框包含具有 modal-header 类名的标题栏元素:

// ✅ 正确 - Antd Modal 自动包含 modal-header 类名
<Modal title="标题">内容</Modal>// ❌ 错误 - 自定义模态框缺少 modal-header 类名
<div className="custom-modal"><div className="title">标题</div> {/* 缺少 modal-header 类名 */}<div>内容</div>
</div>

2. 性能优化建议

  • 避免在 DraggableModal 内部频繁更新状态
  • 对于复杂内容,建议使用 React.memo 优化子组件渲染
const OptimizedContent = React.memo(() => {return (<div>{/* 复杂内容 */}</div>);
});<DraggableModal><Modal title="优化示例"><OptimizedContent /></Modal>
</DraggableModal>

DraggableModal源码

import React, { useState, useRef, useLayoutEffect } from 'react';
import { DndContext, useDraggable } from '@dnd-kit/core';
import type { Coordinates } from '@dnd-kit/utilities';const DraggableWrapper = (props: any) => {const { top, left, children: node, modalRef } = props;const { attributes, isDragging, listeners, setNodeRef, transform } = useDraggable({id: 'draggable-title'});const dragChildren = React.Children.map(node.props.children, (child) => {if (!child) {return child;}if (child.type === 'div' && child.props?.className?.indexOf('modal-header') >= 0) {return React.cloneElement(child, {'data-cypress': 'draggable-handle',style: { cursor: 'move' },...listeners});}return child;});let offsetX = left;let offsetY = top;if (isDragging) {offsetX = left + (transform?.x ?? 0);offsetY = top + transform?.y;}return (<divref={(el) => {setNodeRef(el);if (modalRef) modalRef.current = el;}}{...attributes}style={{transform: `translate(${offsetX ?? 0}px, ${offsetY ?? 0}px)`} as React.CSSProperties}>{React.cloneElement(node, {}, dragChildren)}</div>);
};const DraggableModal = (props: any) => {const [{ x, y }, setCoordinates] = useState<Coordinates>({x: 0,y: 0});const modalRef = useRef<HTMLDivElement>(null);const [modalSize, setModalSize] = useState({ width: 0, height: 0 });useLayoutEffect(() => {if (modalRef.current) {const rect = modalRef.current.getBoundingClientRect();setModalSize({ width: rect.width, height: rect.height });}}, [props.children]);return (<DndContextonDragEnd={({ delta }) => {const winW = window.innerWidth;const winH = window.innerHeight;const needRemoveMinHeight = -100;const needRemoveWidth = (winW - modalRef.current.clientWidth) / 2;const needRemoveMaxHeight = winH - 100 - modalRef.current.clientHeight;const newX = x + delta.x;const newY = y + delta.y;let curNewY = newY;if (newY < 0) {curNewY = newY < needRemoveMinHeight ? needRemoveMinHeight : newY;} else {curNewY = newY > needRemoveMaxHeight ? needRemoveMaxHeight : newY;}if (Math.abs(newX) < needRemoveWidth) {setCoordinates({x: newX,y: curNewY});} else {setCoordinates({x: newX < 0 ? 0 - needRemoveWidth : needRemoveWidth,y: curNewY});}}}><DraggableWrapper top={y} left={x} modalRef={modalRef}>{props.children}</DraggableWrapper></DndContext>);
};export default DraggableModal;

3. 兼容性说明

  • 支持现代浏览器(Chrome 88+、Firefox 84+、Safari 14+)
  • 移动端暂不支持拖拽功能
  • 需要 React 16.8+ 版本支持

故障排除

常见问题

Q: 模态框无法拖拽?

A: 检查以下几点:

  1. 确保模态框标题栏包含 modal-header 类名
  2. 确认 @dnd-kit/core 依赖已正确安装
  3. 检查是否有其他元素阻止了拖拽事件

Q: 拖拽时出现跳跃现象?

A: 这通常是由于 CSS 样式冲突导致,确保没有其他 transform 样式影响模态框定位。

Q: 模态框被拖拽到屏幕外?

A: 组件内置了边界检测,如果出现此问题,请检查窗口大小变化时是否正确触发了重新计算。

版本历史

  • v1.0.0: 初始版本,支持基础拖拽功能和边界检测

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/90009.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90009.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/90009.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL的基本操作及相关python代码

下面为你介绍 MySQL 的基本操作,以及对应的 Python 代码实现。我会先介绍 SQL 基本操作,再展示如何用 Python 连接 MySQL 并执行这些操作。 一、MySQL 基本操作(SQL 语句) 1. 连接数据库 bash mysql -u root -p2. 创建数据库 sql CREATE DATABASE testdb;3. 使用数据…

Armbian(斐讯N1)安装xfce桌面以及远程环境

安装xfce桌面以及vncserver(远程连接) 安装xfce桌面 apt-get install xfce4 xfce4-goodies xorg dbus-x11 x11-xserver-utils ubuntu的安装gdm3&#xff0c; apt install gdm3 debian安装lightdm。 apt install lightdm 安装vnc server apt-get install tightvncserver 中文字体…

【Oracle】Oracle 11g打补丁时遇到opatch apply命令无法识别

⚙️ 1. 使用完整路径执行命令 问题原因&#xff1a;若未将$ORACLE_HOME/OPatch加入系统PATH环境变量&#xff0c;直接输入opatch apply会因系统无法定位命令而报错。 解决方案&#xff1a; 改用绝对路径执行&#xff1a; $ORACLE_HOME/OPatch/opatch apply例如&#xff1a; /u…

单例模式详细讲解

一.定义单例模式是一种创建型设计模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点特点&#xff1a;1.构造函数和析构函数私有化2.禁用拷贝构造函数和赋值运算符重载&#xff08;delete&#xff09;3.利用静态成员函数和静态成员变量来给外界提供访问二…

KORGym:评估大语言模型推理能力的动态游戏平台

KORGym&#xff1a;评估大语言模型推理能力的动态游戏平台 现有评估基准多受领域限制或 pretraining 数据影响&#xff0c;难以精准测LLMs内在推理能力。KORGym平台应运而生&#xff0c;含50余款游戏&#xff0c;多维度评估&#xff0c;本文将深入解析其设计、框架、实验及发现…

ISPDiffuser文章翻译理解

ISPDiffuser: Learning RAW-to-sRGB Mappings with Texture-Aware Diffusion Models and Histogram-Guided Color Consistency翻译 Type: Conference paper Author: Yang Ren1,4, Hai Jiang1,4, Menglong Yang1,2,†, Wei Li1,2, Shuaicheng Liu3,4,† Select: ⭐️⭐️⭐️⭐…

C++线程池执行步骤分析,总结线程池流程

线程池流程总结&#xff1a;1、构造函数中创建线程&#xff0c;并添加到线程池&#xff08;构造函数返回时&#xff0c;线程自动启动&#xff0c;并停在等待wait&#xff1a;从线程池取出一个任务处&#xff09;&#xff1b; 2、主线程中添加任务&#xff0c;到任务队列。并用“…

Java 通过 HttpURLConnection发送 http 请求

问题&#xff1a; 在调试 kill 接口的时候&#xff0c;对方的服务用的是 Django RestFramework 框架提供的接口&#xff0c;用 python 请求时得到的内容如下&#xff1a; ➜ ~ python3 test.py <Response [200]> "true" // 对应的代码是 print(response, r…

【PTA数据结构 | C语言版】列出连通集

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 给定一个有 n 个顶点和 m 条边的无向图&#xff0c;请用深度优先遍历&#xff08;DFS&#xff09;和广度优先遍历&#xff08;BFS&#xff09;分别列出其所有的连通集。假设顶点从 0 到 n−1 编号。…

GoLang教程005:switch分支

3.4 Switch分支 在 GoLand&#xff08;其实是 JetBrains 开发的 Go 编程语言 IDE&#xff09;中&#xff0c;switch 是 Go 语言&#xff08;Golang&#xff09; 的一个重要控制结构&#xff0c;用于替代多个 if-else 语句。 ✅ 特点说明特性说明自动 breakGo 的 switch 语句默认…

uniapp相关地图 API调用

目录 一、 注意事项&#xff1a; manifest.json需增加配置 二、获取用户收货地址 [uni.chooseAddress] 三、获取当前的地理位置、速度 [uni.getLocation] 四、打开地图选择位置、查看位置(导航) [uni.chooseLocation] [uni.openLocation] 五、使用腾讯地图逆地址解析接口实…

Java学习----NIO模型

在 Java 的 I/O 模型中&#xff0c;NIO&#xff08;Non - Blocking I/O&#xff0c;非阻塞 I/O&#xff09;是对 BIO 的重要改进。它为高并发场景提供了更高效的处理方式&#xff0c;在众多 Java 应用中发挥着关键作用。NIO模型的核心在于非阻塞和多路复用&#xff0c;其采用 “…

MySQL计数函数count原理分析

前言 统计表中数据的条数是非常常用的操作,但是咱们常用的InnoDB存储引擎计数函数是现时统计的,所以会出现性能的问题,这次我准备分享计数函数count的原理,保证之后遇到计数方面的问题都可以轻易灵活的解决 与MyISAM存储引擎相比,MyISAM存储引擎是自己记录了表中数据的条数,但…

Day07_网络编程20250721_大项目

基本代码&#xff1a;搭建服务器客户端&#xff0c;要求服务器使用 epoll 模型客户端使用多线程服务器打开数据库&#xff0c;表单格式如下name text primary key pswd text not null客户端做一个简单的界面&#xff1a;1&#xff1a;注册2&#xff1a;登录无论注册还是登录&am…

20250721

P5357 【模板】AC 自动机 - 洛谷 主要是构建fail树 /* 我们可以知道的是&#xff0c;当访问一个点x时&#xff0c;接下来需要跳转其fail[x]&#xff0c;以此类推&#xff0c;如果在某个fail[x]上出现了一个字符串&#xff0c;那么相应的统计次数应该加1&#xff0c;然后当访…

【INT四则优先算式】2022-9-22

缘由ccf201903-2二十四点我用暴力破解做的&#xff0c;但是两个程序一个拿到了满分&#xff0c;一个拿到了50分&#xff0c;看了很长时间也没看出问题在哪里&#xff0c;希望有英雄慧眼帮我看一下-编程语言-CSDN问答 void INT四则优先算式() {//缘由https://ask.csdn.net/ques…

本地k8s集群的搭建

windows机器&#xff0c;考虑如果使用云服务器&#xff0c;每年的开销还是太大&#xff0c;不值得&#xff0c;自己只是做demo&#xff0c;了解各种配置和使用即可&#xff0c;使用VMware的虚拟机来搭建k8s集群 使用docker安装rancher和k8s yum -y install chronycat > /et…

B树、B+树的区别及MySQL为何选择B+树

B树与B树 B树和B树都是自平衡的多路搜索树&#xff0c;广泛应用于数据库和文件系统中&#xff0c;用于高效管理大量数据。它们的设计目标是在磁盘存储环境下减少I/O操作次数&#xff0c;提高数据访问效率。下面我将逐步解释两者的定义、特性、比较以及应用场景&#xff0c;确保…

Unity之可视化编程VisualScripting快速入门

文章目录 前言 脚本机和状态机 脚本图ScriptGraph 脚本图 子图 自定义事件 状态图StateGraph 状态图 Start状态 创建新状态 过渡连接 常用功能 射线检测 补间动画 按钮点击 前言 可视化脚本使您无需编写代码即可为游戏或应用程序创建逻辑。可视化脚本使用基于节点的可视化图形…

2025三掌柜赠书活动第二十五期 网络安全应急响应实战

目录 前言 网络安全的重要性 关于《网络安全应急响应实战》 编辑推荐 内容简介 作者简介 图书目录 《网络安全应急响应实战》全书速览 结束语 前言 在当今数字化时代&#xff0c;网络安全已经成为企业和个人都无法忽视的重要问题。随着网络技术的飞速发展&#xff0c;…