写点什么

在 Electron 中简单实现拖拽功能

作者:废材壶
  • 2021 年 11 月 06 日
  • 本文字数:2239 字

    阅读完需:约 7 分钟

在Electron中简单实现拖拽功能

背景

实现简单的拖拽文件、图片到系统本地,拖拽消息体文本发送(类似于微信、QQ 中的功能)


实现拖拽到本地系统,不考虑mouse实现方式,使用Electron中的startdrag配合BrowserDrag and Drop

startdrag

Electron API (startDrag) 提供拖拽文件到桌面系统本地的能力


使用情况比较简单,主进程监听事件,调用startdrag


// main-processipcMain.on('drag-start', (event) => {    event.sender.startdrag({        file: 'drag-start.md', // string | string[]        icon:'icon.png'    })      return true})
复制代码


子进程 IPC 通信主进程,


// render.jsawait ipcRenderer.invoke('drag-start')// dragend 处理逻辑
复制代码


startdrag的一些问题:


  • 因为startDrag是直接使用V8提供的能力,所以这里会覆盖html5draggable事件,如event.dataTransfer.setData()设置的数据会被覆盖

  • 如果需要监听dragend事件,需要在event.sender.startdrag 后通知到子进程,表示拖拽操作已结束

  • icon定制比较简陋,不能像html5 draggable那样默认使用DOM渲染半透明图

  • startdragWindow系统中一次只能拖动一个文件

  • 鼠标光标手势是个加号图标,等同于 event.dataTransfer.effectAllowed = 'move'

  • windowmacicon 位置有差异,分别在左右两边

HTML5 Drag and Drop

判断存在本地文件路径时,使用startdrag,否则默认执行html5draggable操作


// render.jsconst filePath = getFilePathSync()if(filePath) {    //阻止默认操作    event.preventDefault()    // 发送IPC    await ipcRenderer.invoke('drag-start')    /* 这里可以处理dragend事件逻辑 */    return} else {// html5 draggable处理}
复制代码

draggable 介绍

htm5draggable的事件触发分为dragdrop 两部分



拖拽区域

触发顺序: dragstart > drag(持续触发) > dragend


在元素中绑定dragstart事件监听


const el = document.querySelector('#target')el.addEventListener('dragstart',(e:DragEvent) => {}) 
复制代码


自定义拖拽图像


// 生成拖拽图像function applyDragImage(event: DragEvent,class:string, label:string | null):void {    const dragImage = document.createElement('div')    dragImage.className = class;    dragImage.textContent = label;        if(event.dataTransfer) {        document.body.appendChild(dragImage);        event.dataTransfer.setDragImage(dragImage, -10, -10);                setTimeout(() => document.body.removeChild(dragImage), 0);    } }
复制代码


整体拖拽 && 局部文本选区拖拽


在目标元素的父元素上,设置draggable=true,并且监听目标元素的mouseenter/mouseleave事件,当进入目前元素区域时,取消父元素的draggable=false,离开目标元素区域,恢复父元素的draggable=true



  // 查找父元素   const parent = document.querySelector('#targetParent');   parent.draggable=true      const el = document.querySelector('#target')
// 进入目标元素区域 el.addEventListener('mouseenter', (e:DragEvent) => { parent.removeAttribute('draggable') parent.ondragstart = null },true) // 离开目标元素区域 el.addEventListener('mouseleave', (e:DragEvent) => { parent.draggable = true })
复制代码


拖拽图片到系统本地


    const TYPE = 'image/png'     const NAME = 'name.png'    const URL = 'https://example.com/img/1.png'    // Chrome支持的非标准属性    event.dataTransfer.setData('DownloadURL', `${TYPE}:${NAME}:${URL}`)
复制代码

释放区域

目标元素作为释放区域绑定事件监听



    <template>        <div @dragenter="handleDragEnterFn @dragleave="handleDragLeaveFn" @dragover.prevent @drop="handleDropFn">        /*子组件*/        </div>    </template>        <script lang="ts">        data() {            return {                dropStack: new Set()            }        }        handleDragEnterFn(e:DragEvent) {            this.dropStack.add(e.target)        }                handleDragLeaveFn(e:DragEvent) {            this.dropStack.delete(e.target)            // 栈为空,真正的离开当前区域            if(!this.dropStack.size) {                            }        }                handleDropFn(e:DragEvent) {                }         </script>
复制代码


注意事项:


  • 触发顺序:dragenter -> dragover(持续,每 100 多毫秒重新计算触发) -> dragleave -> drop

  • 通常会有一些进入释放区域和离开释放区域的状态变化,可以放到dragenterdragleave中处理;dragenterdragleave 会包含子子元素的触发响应,需要判断针对对应区域真实的dragleave事件,可以考虑增加dropStack栈存储对应的区域进出,如为空,则表示离开了当前区域。

  • 拖拽操作默认是可以触发自动向上、向下鼠标滚动行为(在获取focus()的情况下)document.querySelector('#dropTarget').focus()

小结

Electron的拖拽操作,大部分还是可以考虑使用 web 实现方式;本地文件(存在本地路径),可以使用startdrag拖拽到本地系统


我是废材壶,前端开发者,欢迎微信搜一搜「 CodeLife 集」阅读不迷路


参考

发布于: 2021 年 11 月 06 日阅读数: 8
用户头像

废材壶

关注

还未添加个人签名 2018.06.09 加入

我是废材壶,前端开发者,欢迎微信搜一搜「 CodeLife集」阅读不迷路

评论

发布
暂无评论
在Electron中简单实现拖拽功能