首页前端开发JavaScript一百多行代码实现react拖拽hooks

一百多行代码实现react拖拽hooks

时间2024-02-01 04:56:03发布访客分类JavaScript浏览604
导读:收集整理的这篇文章主要介绍了一百多行代码实现react拖拽hooks,觉得挺不错的,现在分享给大家,也给大家做个参考。 前言源码总共也就一百多行,看完这个大致可以理解一些成熟的react...
收集整理的这篇文章主要介绍了一百多行代码实现react拖拽hooks,觉得挺不错的,现在分享给大家,也给大家做个参考。

前言

源码总共也就一百多行,看完这个大致可以理解一些成熟的react拖拽库的实现思路,比如react-dnd,然后你上手这些库的时候就非常快了。

使用hooks实现的大致效果动图如下:

我们的目标是实现一个useDrag和useDrop的hooks,类似以下用法就可以轻松让元素可以拖拽,并且在拖拽的各个生命周期,如下,可以自定义传递消息(顺便介绍几个拖拽会触发的事件)。

  • dragstart:用户开始拖拉时,在被拖拉的节点上触发,该事件的target属性是被拖拉的节点。
  • dragenter:拖拉进入当前节点时,在当前节点上触发一次,该事件的target属性是当前节点。通常应该在这个事件的监听函数中,指定是否允许在当前节点放下(drop)拖拉的数据。如果当前节点没有该事件的监听函数,或者监听函数不执行任何操作,就意味着不允许在当前节点放下数据。在视觉上显示拖拉进入当前节点,也是在这个事件的监听函数中设置。
  • dragover:拖拉到当前节点上方时,在当前节点上持续触发(相隔几百毫秒),该事件的target属性是当前节点。该事件与dragenter事件的区别是,dragenter事件在进入该节点时触发,然后只要没有离开这个节点,dragover事件会持续触发。
  • dragleave:拖拉操作离开当前节点范围时,在当前节点上触发,该事件的target属性是当前节点。如果要在视觉上显示拖拉离开操作当前节点,就在这个事件的监听函数中设置。

使用方法 + 源码讲解

class Hello extends React.componentany, any>
 {
 constructor(PRops: any) {
  suPEr(props)  this.state = {
}
 }
 render() {
      return (   DragAndDrop>
        DragElement />
        DropElement />
       /DragAndDrop>
  ) }
}
    ReactDOM.render(Hello />
    , window.document.getElementById("root"))

如上,DragAndDrop组件的作用是给所有的使用useDrag和useDrop的组件传递消息,比如当前拖拽的元素是那个dom,或者你想要其他信息都可以往里面加,我们看看它的实现。

const DragAndDropContext = React.createContext({
 DragAndDropManager: {
}
 }
);const DragAndDrop = ({
 children }
    ) =>
 ( DragAndDropContext.Provider value={
{
 DragAndDropManager: new DragAndDropManager() }
}
    >
  {
children}
     /DragAndDropContext.Provider>
    )

可以看到传递消息是用react的Context的api去实现的,重点就是这个DragAndDropManager,我们看下实现

export default class DragAndDropManager {
 constructor() {
  this.active = null  this.subscriptions = []  this.id = -1 }
 setActive(activeProps) {
      this.active = activeProps  this.subscriptions.foreach((subscription) =>
 subscription.callback()) }
 subscribe(callback) {
  this.id += 1  this.subscriptions.push({
   callback,   id: this.id,  }
)  return this.id }
 unsubscribe(id) {
      this.subscriptions = this.subscriptions.filter((sub) =>
 sub.id !== id) }
}
    

setActive的作用是用来记录当前drag的元素是哪个,useDrag里面会用到,我们在看useDrag的hooks实现的时候就会明白只要调用setActive方法把drag的dom元素传进去,是不是就知道当前拖拽的元素是哪个了呢。

除此之外,我还增加了订阅事件的api,subscribe,目前我并没有使用它,本次示例里你可以忽略这部分,知道可以添加订阅事件就行。

接着我们看看,useDrag的使用,DragElement的实现如下:

function DragElement() {
 const input = useRef(null) const hanleDrag = useDrag({
  ref: input,  collection: {
}
, // 这里可以填写任意你想传递给drop元素的消息,后面会通过参数的形式传递给drop元素 }
) return (  div ref={
input}
    >
   h1 role="button" onClick={
hanleDrag}
    >
        drag元素   /h1>
      /div>
 )}
    

我们就来看下useDrag的实现,非常简单

export default function useDrag(props) {
 const {
 DragAndDropManager }
     = useContext(DragAndDropContext)  const handleDragStart = (e) =>
 {
  DragAndDropManager.setActive(props.collection)  if (e.datatransfer !== undefined) {
   e.dataTransfer.effectAllowed = "move"   e.dataTransfer.dropEffect = "move"   e.dataTransfer.setData("text/plain", "drag") // firefox fix  }
  if (props.onDragStart) {
   props.onDragStart(DragAndDropManager.active)  }
 }
      useEffect(() =>
 {
      if (!props.ref) return () =>
 {
}
  const {
   ref: {
 current }
,  }
 = props  if (current) {
   current.setattribute("draggable", true)   current.addEventListener("dragstart", handleDragStart)  }
      return () =>
 {
   current.removeEventListener("dragstart", handleDragStart)  }
 }
, [props.ref.current]) return handleDragStart}

useDrag做的事情非常简单,

  • 首先通过useContext,来把获取最外层Store的数据,也就是上面代码的DragAndDropManager
  • 在useEffect里面,如果外界传入了ref,就将这个dom元素的属性draggable设为true,也就是可拖拽状态
  • 然后给这个元素绑定dragstart事件,注意了,销毁组件的时候我们要移除事件,以防内存泄漏
  • handleDragStart事件首先把外界传的props.collection更新到我们的外界仓库里,这样每一个要drag,也就是拖拽的元素都可以将我们useDrag中传是入的useDrag({ collection: { } } )信息,通过DragAndDropManager.setActive(props.collection)的方式,传入到外界的store
  • 接着我们dataTransder属性上做一些事,目的是设置元素的拖拽属性为move,并且为了兼容firefox做了处理。
  • 最后每当出发drag事件的时候,外界传入的onDragStart事件也会触发,并且我们将store里的数据传入进去

其中,useDrop的使用,DropElement的实现如下:

function DropElement(props: any): any {
 const input = useRef(null) useDrop({
      ref: input,  // e代表dragOver事件发生时,正在被over的元素的event对象  // collection是store存储的数据  // showAfter是表示,是否鼠标拖拽元素时,鼠标经过drop元素的上方(上方就是上半边,下方就是下半边)  onDragOver: (e, collection, showAfter) =>
 {
  // 如果经过上半边,drop元素的上边框就是红色   if (!showAfter) {
        input.current.style = "border-bottom: none;
border-top: 1px solid red"   }
 else {
        // 如果经过下半边,drop元素的上边框就是红色    input.current.style = "border-top: none;
border-bottom: 1px solid red"   }
  }
    ,  // 如果在drop元素上放开鼠标,则样式清空  onDrop: () =>
 {
   input.current.style = ""  }
    ,  // 如果在离开drop元素,则样式清空  onDragLeave: () =>
 {
   input.current.style = ""  }
, }
    ) return (  div>
   h1 ref={
input}
    >
    drop元素/h1>
      /div>
 )}
    

最后,我们来看看useDrop的实现

export default function useDrop(props) {
// 获取最外层store里的数据 const {
 DragAndDropManager }
     = useContext(DragAndDropContext) const handleDragOver = (e) =>
 {
     // e就是拖拽的event对象  e.preventDefault()  // getBoundingClientRect的图请看下面  const overElementHeight = e.currentTarget.getBoundingClientRect().height / 2  const overElementTopOffset = e.currentTarget.getBoundingClientRect().top  // clientY就是鼠标到浏览器页面可视区域的最顶端的距离  const mousePosITionY = e.clientY  // mousePositionY - overElementTopOffset就是鼠标在元素内部到元素border-top的距离  const showAfter = mousePositionY - overElementTopOffset >
 overElementHeight  if (props.onDragOver) {
   props.onDragOver(e, DragAndDropManager.active, showAfter)  }
 }
     // drop事件 const handledDop = (e: React.DragEvent) =>
 {
  e.preventDefault()  if (props.onDrop) {
   props.onDrop(DragAndDropManager.active)  }
 }
     // dragLeave事件 const handledragLeave = (e: React.DragEvent) =>
 {
  e.preventDefault()  if (props.onDragLeave) {
   props.onDragLeave(DragAndDropManager.active)  }
 }
      // 注册事件,注意销毁组件时要注销事件,避免内存泄露 useEffect(() =>
 {
      if (!props.ref) return () =>
 {
}
  const {
   ref: {
 current }
,  }
 = props  if (current) {
   current.addEventListener("dragover", handleDragOver)   current.addEventListener("drop", handledDop)   current.addEventListener("dragleave", handledragLeave)  }
      return () =>
 {
   current.removeEventListener("dragover", handleDragOver)   current.removeEventListener("drop", handledDop)   current.removeEventListener("dragleave", handledragLeave)  }
 }
, [props.ref.current])}
    

getBoundingClientRect的api图解:

rectObject = object.getBoundingClientRect();

rectObject.top:元素上边到视窗上边的距离;

rectObject.right:元素右边到视窗左边的距离;

rectObject.bottom:元素下边到视窗上边的距离;

rectObject.left:元素左边到视窗左边的距离;

到此这篇关于一百多行代码实现react拖拽hooks的文章就介绍到这了,更多相关react拖拽hooks内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

您可能感兴趣的文章:@H_777_104@
  • 详解gantt甘特图可拖拽、编辑(vue、react都可用 highcharts)
  • typescript+react实现移动端和PC端简单拖拽效果
  • react-beautiful-dnd 实现组件拖拽功能
  • 使用react-beautiful-dnd实现列表间拖拽踩坑
  • React.js组件实现拖拽排序组件功能过程解析
  • React 实现拖拽功能的示例代码
  • react.js组件实现拖拽复制和可排序的示例代码
  • 再次谈论React.js实现原生js拖拽效果引起的一系列问题
  • 基于React.js实现原生js拖拽效果引发的思考
  • react实现简单的拖拽功能

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

上一篇: 浅谈react路由传参的几种方式下一篇:vue实现密码显示隐藏功能的思路详...猜你在找的JavaScript相关文章 html font标签如何设置字体大小?html font标签属性用法介绍2022-05-16vue3+TypeScript+vue-router的使用方法2022-04-16vue3获取当前路由地址2022-04-16如何利用React实现图片识别App2022-04-16JavaScript展开运算符和剩余运算符的区别详解2022-04-16微信小程序中使用vant框架的具体步骤2022-04-16Vue elementUI表单嵌套表格并对每行进行校验详解2022-04-16如何利用Typescript封装本地存储2022-04-16微信小程序中wxs文件的一些妙用分享2022-04-16JavaScript的Set数据结构详解2022-04-16 其他相关热搜词更多phpjavapython程序员loadpost-format-gallery

若转载请注明出处: 一百多行代码实现react拖拽hooks
本文地址: https://pptw.com/jishu/594948.html
ASP.Net 请求响应流程简述 JavaScript实现页面动态验证码的实现示例

游客 回复需填写必要信息