前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue 拖拽hooks ts版

vue 拖拽hooks ts版

作者头像
copy_left
发布2021-01-27 09:53:40
9320
发布2021-01-27 09:53:40
举报
文章被收录于专栏:方球方球
代码语言:javascript
复制
import {
  ref,
} from 'vue'
import { useBool } from './useBool'

export interface AnyFunction {
  (...args: any): any
}

// 数据获取标识
const DATA_SIGN = 'DRAG'

export function getDragDataSign() {
  return DATA_SIGN
}

/**
 * DOM 绑定拖拽事件
 * @returns { { elems, handlers, bindEle } } 
 * - elems Ref 已绑定元素列表
 * - handlers Ref 数据绑定函数
 * - bindEle any => element => void 接收事件传参,返回dom元素收集器
 * @example
 * const { bindEle } = useDrag()
 * const element = document.querySelector('.move-block')
 * element.onDragstart = bindDragstart({id: 1})
 */
export function useDrag(dataSign = DATA_SIGN) {
  const bindDragstart = <T>(data: T) => (e: DragEvent) => {
    /* eslint-disable-next-line no-unused-expressions */
    e.dataTransfer?.setData(dataSign, JSON.stringify(data))
  }

  return { bindDragstart }
}

export interface DragEvents {
  dragover?: (e: DragEvent) => void
  dragenter?: (e: DragEvent) => void
  dragleave?: (e: DragEvent) => void
  dragstart?: (e: DragEvent) => void
  dragend?: (e: DragEvent) => void
  drop?: (e: DragEvent) => void
  paste?: (e: DragEvent) => void
}


export interface DragCallbackType {
  onDom?(domStr: string, e: DragEvent): void
  onUri?(url: string, e: DragEvent): void
  onFiles?(files: DataTransfer['files'], e: DragEvent): void
  onText?(text: string, e: DragEvent): void
  onAny?(e: DragEvent): void
}

// 函数存在既运行
function hasAndRun(fn?: (...args: any) => any, ...args: any[]) {
  if (fn) {
    fn(...args)
  }
}

/**
 * 拖拽区 hook
 * @param { Object } options 拖拽响应回调, 用于处理不同类型数据
 * - onDom dom拖拽释放回调
 * - onUri uri拖拽释放回调
 * - onFiles file拖拽释放回调
 * - onText text拖拽释放回调
 * 
 * @param { Object } events 自定义拖拽事件
 * 
 * @param { string } dataSign 拖拽取值标识
 * 
 * @returns { array  } 
 * - props 拖拽监听函数
 * - isHovering 是否进入监听区
 * 
 * @example
 * --- js
 * const dropType = {
 *   onDom(data){
 *    console.log()
 *   }
 * }
 * const { dragEvents } = useDragArea(dropType)
 * 
 * --- html
 * <div class='area' v-on='dragEvents'>
 * </div>
 */
export function useDragArea(
  dropType = {} as DragCallbackType,
  events = {} as DragEvents,
  dataSign = DATA_SIGN
) {
  const optionsRef = ref(dropType)
  const { state: isHovering, setTrue: startHover, setFalse: endHover } = useBool()
  const { state: isRun, setTrue: startRun, setFalse: endRun } = useBool()

  const callback = (dataTransfer: DataTransfer | null, event: DragEvent) => {
    if (dataTransfer === null) {
      return
    }

    const url = dataTransfer.getData(dataSign)
    const dom = dataTransfer.getData(dataSign)

    const {
      onDom,
      onUri,
      onFiles,
      onText,
      onAny
    } = optionsRef.value

    if (dom && onDom) {
      onDom(JSON.parse(dom), event)
      return
    }

    if (url && onUri) {
      onUri(url, event)
      return
    }

    if (dataTransfer.files && dataTransfer.files.length && onFiles) {
      onFiles(dataTransfer.files, event)
      return
    }

    if (dataTransfer.items && dataTransfer.items.length && onText) {
      dataTransfer.items[0].getAsString((text) => { onText(text, event) })
      return
    }

    if (onAny) {
      onAny(event)
    }
  }

  const {
    dragover,
    dragenter,
    dragleave,
    dragstart,
    dragend,
    paste,
    drop
  } = events

  const dragEvents = {
    dragover: (e: DragEvent) => {
      e.preventDefault()
      e.stopPropagation()
      hasAndRun(dragover, e)
    },
    dragenter: (e: DragEvent) => { 
      e.preventDefault()
      e.stopPropagation()
      startHover()
      hasAndRun(dragenter, e)
    },
    dragleave: (e: DragEvent) => { 
      e.preventDefault()
      e.stopPropagation()
      endHover()
      hasAndRun(dragleave, e)
    },
    dragstart: (e: DragEvent) => { 
      e.preventDefault()
      e.stopPropagation()
      startRun()
      hasAndRun(dragstart, e)
    },
    dragend: (e: DragEvent) => { 
      e.preventDefault()
      e.stopPropagation()
      endRun()
      hasAndRun(dragend, e)
    },
    paste: (e: DragEvent) => {
      callback(e.dataTransfer, e)
      hasAndRun(paste, e)
    },
    drop: (e: DragEvent) => {
      e.preventDefault()
      e.stopPropagation()
      endHover()
      callback(e.dataTransfer, e)
      hasAndRun(drop, e)
    }
  }

  return {
    dragEvents,
    isHovering,
    isRun
  }
}

注意点

  • 拖拽属性必须明确设置为 draggable='true' 短写模式无效 dragg
  • 需配置 dragover 事件,否则拖拽区内将出现禁止提示
  • 事件设置 e.preventDefault
  • 事件设置 e.stopPropagation 防止嵌套拖拽时,触发父元素事件
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 注意点
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档