当前位置: 首页 > news >正文

react 实现拖动元素

demo使用create-react-app脚手架创建
删除一些文件,创建一些文件后
结构目录如下截图

在这里插入图片描述

com/index
import Movable from './move'
import { useMove } from './move.hook'
import * as Operations from './move.op'Movable.useMove = useMove
Movable.Operations = Operationsexport default Movable
com/move
import React, {forwardRef, memo} from "react"
import { noop } from "../utils/noop"
import {mouseTracker, touchTracker, moveTracker} from './move.utils';
// forwardRef 将允许组件使用ref,将dom暴露给父组件; 返回一个可以接受ref属性的组件
export const Move = forwardRef(({onBeginMove, onMove, onEndMove, ...props}, ref) =>  {const tracker = moveTracker(onBeginMove, onMove, onEndMove);const handleOnMouseDown = mouseTracker(tracker);return <div {...props} ref={ref}onMouseDown={handleOnMouseDown}className={`movable ${props.className}`}/>
})export default memo(Move)
com/move.utilsexport const moveTracker = (onBeginMove, onMove, onEndMove) => {let initial = {};let previous = {};const event = e => ({...e,cx: e.x - previous.x,cy: e.y - previous.y,dx: e.x - initial.x,dy: e.y - initial.y,});return {start: e => {initial = {x: e.x, y: e.y};previous = {...initial};onBeginMove(event(e));},move: e => {onMove(event(e));previous = {x: e.x, y: e.y};},end: e => {onEndMove(event(e));},}
};export const mouseTracker = tracker => {const event = e => ({x: e.clientX,y: e.clientY,target: e.target,stopPropagation: () => e.stopPropagation(),preventDefault: () => e.preventDefault(),});const onMouseDown = e => {document.addEventListener('mousemove', onMouseMove);document.addEventListener('mouseup', onMouseUp);tracker.start(event(e));};const onMouseMove = e => {tracker.move(event(e));};const onMouseUp = e => {document.removeEventListener('mousemove', onMouseMove);document.removeEventListener('mouseup', onMouseUp);tracker.end(event(e));};return onMouseDown;
};
com/move.hook
import { useRef, useCallback } from "react";export const useMove = ops => {const shared = useRef({})const onBeginMove = useCallback(e => {ops.forEach(({onBeginMove}) => onBeginMove(e, shared.current));}, [ops])const onMove = useCallback(e => {ops.forEach(({onMove}) => onMove(e, shared.current));}, [ops])const onEndMove = useCallback(e => {ops.forEach(({onEndMove}) => onEndMove(e, shared.current));}, [ops])return {onBeginMove, onMove, onEndMove}
}
com/move.op
import { clamp } from "../utils/number";
import { noop } from "../utils/noop";
import { isEqual } from "../utils/object";export const createOp = handlers => ({onBeginMove: noop,onMove: noop,onEndMove: noop,...handlers
})export const move = m => createOp({onBeginMove: (e, shared) => {// getBoundingClientRect返回一个 DOMRect 对象,其提供了元素的大小及其相对于视口的位置。const { top, left } = m.current.getBoundingClientRect()shared.next = {top, left}shared.initial = {top, left}},onMove: ({dx, dy}, shared) => {const {left, top} = shared.initialshared.next = {left: left + dx,top: top + dy}}
})export const update = onUpdate => createOp({onBeginMove: _update(onUpdate),onMove: _update(onUpdate),onEndMove: _update(onUpdate),
});
const _update = onUpdate => (e, shared) => {if (!isEqual(shared.prev, shared.next)) {onUpdate(shared.next);shared.prev = shared.next;}
};
utils/number
export const clamp = (num, min, max) => {return Math.min(Math.max(num, min), max)
}
================================
utils/noop
export const noop = () => null;
================================
utils/objectconst Types = {NUMBER: 'number',OBJECT: 'object',NULL: 'null',ARRAY: 'array',UNDEFINED: 'undefined',BOOLEAN: 'boolean',STRING: 'string',DATE: 'date',
};
const getType = v => Object.prototype.toString.call(v).slice(8, -1).toLowerCase();
const isType = (v, ...types) => types.includes(getType(v));
const isObject = v => isType(v, Types.OBJECT);
const isArray = v => isType(v, Types.ARRAY);export const EqualityIterators = {SHALLOW: (a, b) => a === b,DEEP: (a, b, visited = []) => {if (visited.includes(a)) {return true;}if (a instanceof Object) {visited.push(a);}return isEqual(a, b, (a, b) => EqualityIterators.DEEP(a, b, visited))},
};export const isEqual = (a, b, iterator = EqualityIterators.DEEP) => {if (a === b) {return true;}if (getType(a) !== getType(b)) {return false;}if (isObject(a) && Object.keys(a).length === Object.keys(b).length) {return Object.keys(a).every(key => iterator(a[key], b[key]));}if (isArray(a) && a.length === b.length) {return a.every((item, i) => iterator(a[i], b[i]))}return false;
};
App.js
import { useMemo, useRef, useState } from "react";
import Movable from "./com";const {move, update} = Movable.Operationsfunction App() {const ref = useRef()const ref2 = useRef()const [p, setP] = useState({})const [p2, setP2] = useState({})const props = Movable.useMove(useMemo(() => [move(ref),update(setP)], []))const props2 = Movable.useMove(useMemo(() => [move(ref2),update(setP2)], []))return (<><Movable {...props} ref={ref} style={p}>拖我</Movable><Movable {...props2} ref={ref2} style={p2}>拖我2</Movable></>);
}export default App;
src/index
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode><App /></React.StrictMode>
);// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
src/index.css
.movable {user-select: none;width: 100px;height: 100px;cursor: move;position: absolute;padding: 10px;display: flex;align-items: center;justify-content: center;text-align: center;background-color: palegreen;
} 

效果截图如下
在这里插入图片描述

相关文章:

react 实现拖动元素

demo使用create-react-app脚手架创建 删除一些文件&#xff0c;创建一些文件后 结构目录如下截图com/index import Movable from ./move import { useMove } from ./move.hook import * as Operations from ./move.opMovable.useMove useMove Movable.Operations Operationse…...

【EI会议】第二届声学,流体力学与工程国际学术会议(AFME 2023)

第二届声学&#xff0c;流体力学与工程国际学术会议 2023 2nd International Conference on Acoustics, Fluid Mechanics and Engineering&#xff08;AFME 2023&#xff09; 声学、流体力学两个古老的学科发展至今&#xff0c;无时无刻都在影响着我们的生活。小到日常使用的耳…...

Android StringFog 字符串自动加密

一、StringFog 作用 一款自动对dex/aar/jar文件中的字符串进行加密Android插件工具&#xff0c;正如名字所言&#xff0c;给字符串加上一层雾霭&#xff0c;使人难以窥视其真面目。可以用于增加反编译难度&#xff0c;防止字符串代码重复。 支持java/kotlin。支持app打包生成…...

上四休三,未来的期许

近日“少上一天班&#xff0c;究竟香不香”引发关注&#xff0c;英国媒体2月21日报道&#xff0c;一项全世界目前为止参加人数最多的“四天工作制”试验&#xff0c;不久前在英国取得了成功。很多人表示上过四天班之后&#xff0c;给多少钱也回不去五天班的时代了。 来百度APP畅…...

怎么防止360安全卫士修改默认浏览器?

默认的浏览器 原先选项是360极速浏览器&#xff08;如果有安装的话&#xff09;&#xff0c;我这里改成了Chrome。 先解锁 才能修改。...

调整参数提高mysql读写速度

要提升MySQL的写入速度,您可以采取一些参数调整和优化措施,这些措施可以根据您的具体应用和环境进行调整。以下是一些常见的参数和优化建议: InnoDB存储引擎: 如果您使用的是InnoDB存储引擎,确保以下参数被设置得合理: innodb_buffer_pool_size:增加内存池大小,以便更多…...

第一届电子纸产业创新应用论坛-邀请函

...

Go expvar包

介绍与使用 expvar 是 exposed variable的简写 expvar包[1]是 Golang 官方为暴露Go应用内部指标数据所提供的标准对外接口&#xff0c;可以辅助获取和调试全局变量。 其通过init函数将内置的expvarHandler(一个标准http HandlerFunc)注册到http包ListenAndServe创建的默认Serve…...

Yolo v8代码逐行解读

train.py文件 1.FILE Path(__file__).resolve() __file__代表的是train.py文件&#xff0c;Path(__file__).resolve()结果是train.py文件的绝对路径。 2.ROOT FILE.parents[0] 获得train.py父目录的绝对路径 3.sys.path 是一个列表list&#xff0c;里面包含了已经添加到系…...

9.18号作业

完善登录框 点击登录按钮后&#xff0c;判断账号&#xff08;admin&#xff09;和密码&#xff08;123456&#xff09;是否一致&#xff0c;如果匹配失败&#xff0c;则弹出错误对话框&#xff0c;文本内容“账号密码不匹配&#xff0c;是否重新登录”&#xff0c;给定两个按钮…...

Spring源码阅读(spring-framework-5.2.24)

spring-aop spring-aspects spring-beans spring-context 等等 第一步&#xff1a; Tags spring-projects/spring-framework GitHub 找到相应的release版本 第二步&#xff1a; 下载相应版本的gardle&#xff0c;如何看版本 spring-framework/gradle/wrapper /gradl…...

【SpringMVC】文件上传与下载、JREBEL使用

目录 一、引言 二、文件的上传 1、单文件上传 1.1、数据表准备 1.2、添加依赖 1.3、配置文件 1.4、编写表单 1.5、编写controller层 2、多文件上传 2.1、编写form表单 2.2、编写controller层 2.3、测试 三、文件下载 四、JREBEL使用 1、下载注册 2、离线设置 一…...

数据结构 第二章作业 线性表 西安石油大学

在顺序表中插入和删除一个结点需平均移动多少个结点&#xff1f;具体的移动次数取决于 哪两个因素&#xff1f; 在顺序表中插入和删除一个结点时&#xff0c;平均移动的结点数量取决于两个因素&#xff1a;插入/删除位置和当前顺序表的长度。 插入/删除位置&#xff1a;如果要…...

vue.mixin全局混合选项

在Vue.js中&#xff0c;Vue.mixin 是一个用来全局混合(mixin)选项的方法。它允许你在多个组件中共享相同的选项&#xff0c;例如数据、方法、生命周期钩子等。这可以用来在组件之间重复使用一些逻辑或共享一些通用的功能 Vue.mixin({// 在这里定义混合的选项data() {return {s…...

VMware Fusion 13+Ubuntu ARM Server 22.04.3在M2芯片的Mac上共享文件夹

因为Server版没有桌面&#xff0c;VMware Tools不能直接装&#xff0c;导致没办法共享文件。 Ubuntu中的包如果需要更新&#xff0c;先执行下面的步骤 sudo apt update 再执行 sudo apt upgrade 不需要更新的话&#xff0c;直接执行下面的步骤 先把open-vm-tools卸载了 …...

PostgreSQL serial类型

serial类型和序列 postgresql序列号&#xff08;SERIAL&#xff09;类型包括 smallserial&#xff08;smallint,short&#xff09;,serial(int)bigserial(bigint,long long int) 不管是smallserial,serial还是bigserial&#xff0c;其范围都是(1,9223372036854775807)&#…...

[创业之路-76] - 创业公司如何在长期坚持中顺势而为?诚迈科技参观交流有感

目录 一、创业环境 1.1. VUCA乌卡时代&#xff1a;易变、复杂、不确定性、模糊的时代 1.2. 中国用了四十年的时间完成了三次工业革命&#xff1a;机械化、电气化、数字化 1.3. 中国正在经历着第四次工业革命&#xff1a;智能化、生态化、拟人化 1.4 国产替代&#xff1a;国…...

人脸修复祛马赛克算法CodeFormer——C++与Python模型部署

一、人脸修复算法 1.算法简介 CodeFormer是一种基于AI技术深度学习的人脸复原模型&#xff0c;由南洋理工大学和商汤科技联合研究中心联合开发&#xff0c;它能够接收模糊或马赛克图像作为输入&#xff0c;并生成更清晰的原始图像。算法源码地址&#xff1a;https://github.c…...

linux入门到精通-第三章-vi(vim)编辑器

目录 文本编辑器gedit介绍vi(vim)命令模式命令模式编辑模式末行模式 帮助教程保存文件切换到编辑模式光标移动(命令模式下)复制粘贴删除撤销恢复保存退出查找替换可视模式替换模式分屏其他用法配置文件 文本编辑器 gedit介绍 gedit是一个GNOME桌面环境下兼容UTF-8的文本编辑器…...

Mybatis面试题(三)

文章目录 前言一、Xml 映射文件中&#xff0c;除了常见的 select|insert|updae|delete 标签之外&#xff0c;还有哪些标签&#xff1f;二、当实体类中的属性名和表中的字段名不一样&#xff0c;如果将查询的结果封装到指定 pojo&#xff1f;三、模糊查询 like 语句该怎么写四、…...

Qt扩展-KDDockWidgets 简介及配置

Qt扩展-KDDockWidgets 简介及配置] 一、概述二、编译 KDDockWidgets 库1. Cmake Gui 中选择源文件和编译后的路径2. 点击Config&#xff0c;配置好编译器3. 点击Generate4. 在存放编译的文件夹输入如下命令开始编译 三、qmake 配置 一、概述 kdockwidgets是一个由KDAB组织编写…...

Vue3搭配Element Plus 实现候选搜索框效果

直接上代码 <el-col :span"14" class"ipt-col"><el-input v-model"projectName" class"w-50 m-2" input"inputChange" focus"inputFocusFn" blur"inputBlurFn" placeholder"请输入项目名…...

进程间的通信方式

文章目录 1.简单介绍2.管道2.1管道的基础概念**管道读写规则**:**管道特点** 2.2匿名管道匿名管道父子进程间通信的经典案例&#xff1a; 2.3命名管道基本概念:命名管道的创建&#xff1a;命名管道的打开规则&#xff1a;匿名管道与普通管道的区别**例子&#xff1a;用命名管道…...

分类预测 | Matlab实现基于MIC-BP-Adaboost最大互信息系数数据特征选择算法结合Adaboost-BP神经网络的数据分类预测

分类预测 | Matlab实现基于MIC-BP-Adaboost最大互信息系数数据特征选择算法结合Adaboost-BP神经网络的数据分类预测 目录 分类预测 | Matlab实现基于MIC-BP-Adaboost最大互信息系数数据特征选择算法结合Adaboost-BP神经网络的数据分类预测效果一览基本介绍研究内容程序设计参考…...

phpcms v9对联广告关闭左侧广告

修改目录“\caches\poster_js”下的文件“53.js”&#xff0c;修改函数“showADContent()” 将代码&#xff1a; str "<div idPCMSAD_"this.PosID"_"i" style"align_b":"x"px;top:"y"px;width:"this.Width&…...

7.2.4 【MySQL】匹配范围值

回头看我们 idx_name_birthday_phone_number 索引的 B 树示意图&#xff0c;所有记录都是按照索引列的值从小到大的顺序排好序的&#xff0c;所以这极大的方便我们查找索引列的值在某个范围内的记录。比方说下边这个查询语句&#xff1a; SELECT * FROM person_info WHERE nam…...

1400*C. No Prime Differences(找规律数学)

解析&#xff1a; 由于 1 不是质数&#xff0c;所以我们令每一行的数都相差 1 对于行间&#xff0c;分为 n、m之中有存在偶数和都为奇数两种情况。 如果n、m存在偶数&#xff0c;假设m为偶数。 如果都为奇数&#xff0c;则&#xff1a; #include<bits/stdc.h> using name…...

Python基础之装饰器

文章目录 1 装饰器1.1 定义1.2 使用示例1.2.1 使用类中实例装饰器1.2.2 使用类方法装饰器1.2.3 使用类中静态装饰器1.2.4 使用类中普通装饰器 1.3 内部装饰器1.3.1 property 2 常用装饰器2.1 timer:测量执行时间2.2 memoize:缓存结果2.3 validate_input:数据验证2.4 log_result…...

IDEA设置Maven 镜像

第一步&#xff1a;右键项目&#xff0c;选择Maven->Create ‘settings.xml’ 已经存在的话是Open ‘settings.xml’&#xff1a; 第二步&#xff1a;在settings.xml文件中增加阿里云镜像地址&#xff0c;代码如下&#xff1a; <?xml version"1.0" encodin…...

项目评定等级L1、L2、L3、L4

软件项目评定等级的数量可以因不同的评定体系和标准而异。一般情况下&#xff0c;项目评定等级通常按照项目的规模、复杂性和风险等因素来划分&#xff0c;可以有多个等级&#xff0c;常见的包括&#xff1a; L1&#xff08;Level 1&#xff09;&#xff1a;通常表示较小规模、…...

服装网站建设进度及实施过程/产品seo怎么优化

浮点数的表示和精度如果a>0&#xff0c;那么1a一定大于1吗&#xff1f;在数学上&#xff0c;答案是肯定的。但在计算机上&#xff0c;答案就与a的大小和浮点数的精度有关了。在matalb上&#xff0c;可以作以下计算&#xff1a;>> a1/2^52a 2.220446049250313e-016>…...

平面设计类的网站/友情链接买卖

SQLite分页显示&#xff1a;Select * From news order by id desc Limit 10 Offset 10这篇文章是根据 SQLite 官方 WIKI 里的内容翻译&#xff0c;如果有什么翻译不当的地方希望大家指出&#xff0c;毕竟我的英文水平实在很差。 SQLite 包括以下五个时间函数&#xff1a; date(…...

wordpress的集成环境/网站推广的方式有哪些

转载于:https://www.cnblogs.com/jkwang/p/5841234.html...

做pc端网站平台/百度seo优化是做什么的

成熟又有钱的是父亲&#xff0c;成熟又没钱的是舅舅&#xff0c;不成熟有没钱的是儿子&#xff0c;看来自己只能做个儿子了。...

免费微信小程序制作/百度seo流量

链表05--复杂链表的复制-jz25题目概述解析&参考答案注意事项说明题目概述 算法说明 输入一个复杂链表&#xff08;每个节点中有节点值&#xff0c;以及两个指针&#xff0c;一个指向下一个节点&#xff0c;另一个特殊指针random指向一个随机节点&#xff09;&#xff0c;请…...

wordpress 标签数量/做个网站

最基础的收集算法 —— 标记/清除算法 之所以说标记/清除算法是几种GC算法中最基础的算法&#xff0c;是因为后续的收集算法都是基于这种思路并对其不足进行改进而得到的。标记/清除算法的基本思想就跟它的名字一样&#xff0c;分为“标记”和“清除”两个阶段&#xff1a;首先…...