React 模态框的设计(三)拖动组件的完善
我在上次的Draggable组件的设计中给了一个简化的方法,今天我来完善一下这个组件,可用于任何可移动组件的包裹。完善后的效果如下所示:

这个优化中,增加了一个注目的效果,还增加了触发可拖动区域的指定功能,这样我们对可拖动组件有更大的自由掌控。
在 Draggable 中增加以下两个Props
-
enableHandler是否启用可拖动句柄 -
draggableHandler可拖动句柄字符
上面的draggableHandler就是一个类名,如果 enableHandler 为true的情况下,可拖动组件中含有 draggableHandler 指定的类名的子组件的 mouseDown 事件会触发拖动的移动效果。其实触发事件是通过Draggable代理实现的。
import React, { useEffect, useRef, useState } from 'react';
import Box from '@mui/material/Box';/*** 拖动组件* @param {是否启用拖动句柄 } enableHandler * @param {拖动句柄的类名} draggableHandler*/
export default function Draggable({children, // 子组件enableHandler = false, // 是否启用拖动句柄draggableHandler = ".modelHandler" // 拖动句柄的类名
}) {const [isDragging, setIsDragging] = useState(false); // 是否正在拖动const [canDrag, setCanDrag] = useState(!enableHandler); // 是否可以拖动const [position, setPosition] = useState({ x: 0, y: 0 }); // 位置const offsetX = useRef(0); // x轴偏移量const offsetY = useRef(0); // y轴偏移量useEffect(() => {// 鼠标移动事件const handleMouseMove = (e) => {if (isDragging) {setPosition({x: e.clientX - offsetX.current,y: e.clientY - offsetY.current});}};// 鼠标抬起事件const handleMouseUp = (e) => {if(e.button !== 0) return;setIsDragging(false);};// 在相关的事件委托到document上if (isDragging) {document.addEventListener('mousemove', handleMouseMove);document.addEventListener('mouseup', handleMouseUp);} else {document.removeEventListener('mousemove', handleMouseMove);document.removeEventListener('mouseup', handleMouseUp);}// 组件卸载时移除事件return () => {document.removeEventListener('mousemove', handleMouseMove);document.removeEventListener('mouseup', handleMouseUp);};}, [isDragging]);const onMouseMove = (e) => {if (enableHandler) {// 判断是否在拖动句柄上if (document.elementFromPoint(e.clientX, e.clientY).className.includes(draggableHandler)) {setCanDrag(true);} else {setCanDrag(false);}}}const handleMouseDown = (e) => {e.preventDefault();e.stopPropagation();if (enableHandler) {// 判断是否在拖动句柄上if (document.elementFromPoint(e.clientX, e.clientY).className.includes(draggableHandler)) {if (e.button !== 0) return;setIsDragging(true);offsetX.current = e.clientX - position.x;offsetY.current = e.clientY - position.y;}} else {if (e.button !== 0) return;setIsDragging(true);offsetX.current = e.clientX - position.x;offsetY.current = e.clientY - position.y;}};return (<Boxsx={{position: 'relative',transform: `translate(${position.x}px, ${position.y}px)`,cursor: canDrag ? isDragging ? "grabbing" : "grab" : "default",transition: `transform:`,}}onMouseDown={handleMouseDown}onMouseMove={onMouseMove}><Boxsx={{transform: `${isDragging ? "scale(1.03)" : "scale(1)"}`,transition: `transform 200ms ease-in-out`,}}>{children}</Box></Box>);
}
上面的逻辑并不复杂,通过过下面的语句可以找到含有指定类名的组件:
document.elementFromPoint(e.clientX, e.clientY).className.includes(draggableHandler)
这样就能判断当前鼠标是否处于指定的组件上并启动移动效果。 由于我们要实现抓取注目动画和移动动画,都是通过 transform实现的,但是我们只要缩放动画,所以我用了两层Box包裹来分割transform属性。
为了测试这个Draggable, 我来做个小组件测试 draggableHandler 的作用。
/** @jsxImportSource @emotion/react */
import { css, jsx } from '@emotion/react'
import Box from '@mui/material/Box';const titleBarCss = css`background-color: #BDBDBD;padding: 8px;`;const modelContentCss = css`padding: 16px;`;const ModalTest = ({width = 400, height = 300, bgColor = "white"}) => {return (<Box css={css`position: relative;background-color: ${bgColor};border: 1px solid #ccc;border-radius: 5px;overflow: hidden;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);width: ${width}px;height: ${height}px;`}><Boxcss={titleBarCss}className=".modelHandler">这是标题</Box><Box css={modelContentCss}>这是弹窗内容</Box></Box>);
};export default ModalTest;
上面的组件中,我们在标题栏上增加了类名: modelHandler。很简单是不是。接下来我们来完整的测试:
import React from "react";
import Stack from "@mui/material/Stack";
import Draggable from "../../framework-kakaer/SModel/_Draggable";
import ModelTest from "../../framework-kakaer/SModel/_DraggableContent";export default function DraggableTest() {return (<Stack spacing={3}><Stack direction="row" spacing={2}><Draggable><div style={{ width: 200, height: 200, backgroundColor: 'red' }}>Draggable</div></Draggable><Draggable><div style={{ width: 200, height: 200, backgroundColor: 'green' }}>Draggable</div></Draggable><Draggable><div style={{ width: 200, height: 200, backgroundColor: 'blue' }}>Draggable</div></Draggable></Stack><Stack direction="row" spacing={2}><Draggable enableHandler={true}><ModelTest width={200} height={200} bgColor="yellow" /></Draggable><Draggable><ModelTest width={200} height={200} bgColor="#FF9500" /></Draggable><Draggable><ModelTest width={200} height={200} bgColor="#5AC8FA" /></Draggable></Stack></Stack>);
}
这样就有了开头的效果了。相当的完美是不是。
相关文章:
React 模态框的设计(三)拖动组件的完善
我在上次的Draggable组件的设计中给了一个简化的方法,今天我来完善一下这个组件,可用于任何可移动组件的包裹。完善后的效果如下所示: 这个优化中,增加了一个注目的效果,还增加了触发可拖动区域的指定功能,…...
wondows10用Electron打包threejs的项目记录
背景 电脑是用的mac,安装了parallels desktop ,想用electron 想同时打包出 苹果版本和windows版本。因为是在虚拟机里安装,它常被我重装,所以记录一下打包的整个过程。另外就是node生态太活跃,几个依赖没记录具体版本࿰…...
git的master、develop、feature分支分别是做什么用的?有什么区别和联系?
在Git版本控制系统中,master、develop和feature分支都是常用的分支类型,它们有不同的用途和特点。 master分支:master分支是Git默认的主分支,它包含了项目的稳定版本。通常,master分支用于发布正式版本,即经…...
前端基础面试题
摘要:最近,看了下慕课2周刷完n道面试题,记录下... 1.请说明Ajax、Fetch、Axios三者的区别 三者都用于网络请求,但维度不同: Ajax(Asynchronous Javascript ang XML),是一种在不重新…...
docker自定义网络实现容器之间的通信
Background docker原理 docker是一个Client-Server结构的系统,Docker的守护进程运行在主机上。通过Socket从客户端访问。docker核心三大组件:image–镜像、container-容器、 repository-仓库。docker使用的cpu、内存以及系统内核等资源都是直接使用宿主…...
NLP_构建GPT模型并完成文本生成任务
文章目录 搭建GPT模型(解码器)构建文本生成任务的数据集训练过程中的自回归文本生成中的自回归(贪婪搜索)完整代码小结 搭建GPT模型(解码器) GPT 只使用了 Transformer的解码器部分,其关键组件…...
使用puppeteer完成监听浏览器下载文件并保存到自己本地或服务器上完成上传功能
需求场景 获取网站点击的下载pdf,并把pdf重命名再上传到COS云上面 技术使用 “puppeteer”: “^19.7.2”, “egg”: “^3.15.0”, // 服务期用egg搭的 文件服务使用COS腾讯云 核心思路 获取浏览器下载事件,并把文件保存到本地 const session awai…...
软件压力测试:测试方法与步骤详解
随着软件应用的不断发展,用户对系统性能的要求也逐渐提高。在不同的负载条件下,系统必须能够保持稳定、高效的运行。软件压力测试是一种验证系统在各种负载情况下性能表现的关键手段。本文将详细探讨软件压力测试的方法和步骤。 1. 明确测试目标 在进行压…...
Oerlikon欧瑞康LPCVD system操作使用说明
Oerlikon欧瑞康LPCVD system操作使用说明...
pyspark统计指标计算
下面介绍如何使用pyspark处理计算超大数据的统计指标,主要为:最大值、最小值、均值、方差、标准差、中位数、众数、非重复值等。 # 加载稽核数据 rd_sql f"select * from database.table" spark_data spark.sql(rd_sql)# 计算众数 由于spar…...
2.22号qt
1.使用信号和槽实现多个界面跳转 1.1准备两个界面 1.2第一个界面准备signal 1.3第二个界面准备slot 1.4将第一个界面的信号和槽进行连接 2.qss登录界面升级优化 2.1概念 Qss是Qt程序界面中用来设置控件的背景图片、大小、字体颜色、字体类型、按钮状态变化等属性ÿ…...
$attrs
一、概念 vue官网定义如下: 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过v-bind="$attrs"传入内部组件——在创建…...
OS X(MACOS) C/C++ 遍历系统所有的IP路由表配置。
以下源实现为遍历MAC苹果电脑系统上配置的所有IP路由表配置,回调 predicate 过滤函数只在 AF_INET(IPV4)的时候跳出,其它时不处理,人们可以根据自己的需求改动。 无需依赖MAC OS框架库提供的函数,最小依赖才有可能更容易移植代码到…...
人工智能_普通服务器CPU_安装清华开源人工智能AI大模型ChatGlm-6B_003---人工智能工作笔记0098
前面的环境安装差不多了,这里我没有安装git,因为我认为用不到,好下面去下载算法: 首先是算法下载: https://codeload.github.com/THUDM/ChatGLM-6B/zip/refs/heads/main 算法的下载连接是这里: 可以看到下载以后得到这个ChatGLM-6B-main这个算法压缩包 然后我们再去: 然后…...
基于JAVA的实验室耗材管理系统 开源项目
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 耗材档案模块2.2 耗材入库模块2.3 耗材出库模块2.4 耗材申请模块2.5 耗材审核模块 三、系统展示四、核心代码4.1 查询耗材品类4.2 查询资产出库清单4.3 资产出库4.4 查询入库单4.5 资产入库 五、免责说明 一、摘要 1.1…...
NXP实战笔记(七):S32K3xx基于RTD-SDK在S32DS上配置ICU输入捕获
目录 1、概述 2、输入捕获SDK配置 2.1、SAIC中断方式 2.2、IPWM或者IPM 1、概述 输入捕获,可以抓取高电平时间、低电平时间、占空比、周期、边沿检测与回调函数、边沿计数(ABZ解码)、时间戳、唤醒中断。 记录一下根据Emios模块实现上述部分…...
左右联动布局效果
效果图: <template><el-dialog :modelValue"modelValue" :before-close"close" fullscreen :close-on-click-modal"false"><div class"farmer_detail"><div class"info_content"><di…...
【工具类】vscode ssh 远程免密登录开发
存放代码的机器运行 sshd,使用 vscode 的机器保证可以通过 ssh 登录服务器vscode 机器通过 ssh-keygen 生成 ssh 公私钥对将客户端的 id_rsa.pub 加入到服务器的鉴权队列 cat id_rsa.pub >> authorized_keysvscode 配置即可.ctrlp, remote-ssh: open ssh configuration f…...
【Antd】Form 表单获取不到 Input 的值
文章目录 今天遇到了一个奇怪的bug,Form表单中的Input组件的值,不能被Form获取,导致输入了内容,但是表单提交的时候值为undefined 报错代码 import { Button, Form, Input } from antd; import React from react;const App: Rea…...
Encoder-decoder 与Decoder-only 模型之间的使用区别
承接上文:Transformer Encoder-Decoer 结构回顾 笔者以huggingface T5 transformer 对encoder-decoder 模型进行了简单的回顾。 由于笔者最近使用decoder-only模型时发现,其使用细节和encoder-decoder有着非常大的区别;而huggingface的接口为…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
