使用TypeScript给Vue 3.0写一个指令实现组件拖拽
最近在用vue3重构后台的一个功能。一个弹窗组件,弹出一个表单。然后点击提交。
早上运维突然跑过来问我,为啥弹窗挡住了下边的表格的数据,我添加的时候,都没法对照表格来看了。你必须给我解决一下。
我参考了一下几大Vue的ui组件库。发现element iview antv。好像都没这个功能。为啥运维需要这个功能??
但是没办法,只能整一个就是了。
做之前本来想直接做到dialog这个组件中。但是又担心后面其他的组件会用到。于是决定把拖拽功能做到指令中。

整个功能点如图。鼠标在拖拽区域拖动,整个对话框在浏览器可视范围内移动。
Drag指令主要实现思路
在指令挂载的时候,监听当前html节点的鼠标点击事件
然后在点击当前html节点的时候,判断是否点击在drag-target这个class所在的子节点上。如果是,那么触发document鼠标移动事件。然后计算出鼠标移动距离,对应修改弹出框的left值和top值。并记录下当前按下的位置x和y
let x = e.clientX;
let y = e.clientY;
如何限制拖动的节点只能在屏幕内移动,不能移动出屏幕呢?
限制left不能小于0,在定位为position: fixed 的时候,left如果小于0,那么html节点的左侧肯定已经在显示区域外了。那么我们不能让left小于0
let bodyW = document.body.clientWidth;
let bodyH = document.body.clientHeight; let left = elLeft - (x - move.clientX);
if (left < 0) {
left = 0;
}
限制left不能大于可视区域的宽度减去当前html节点的宽度,如果left大于这个宽度,那么当前html节点肯定右侧已经处于显示区域的右侧外边了
if (left > bodyW - el.offsetWidth) {
left = bodyW - el.offsetWidth;
}
上下拖拽位置限制和左右拖拽限制思路是一样,只要保证top的值大于0且小于屏幕可视范围的高度减去当前html节点的高度,那么拖动就无法拖出屏幕了。
let top = elTop - (y - move.clientY);
if (top < 0) {
top = 0;
}
if (top > bodyH - el.offsetHeight) {
top = bodyH - el.offsetHeight
}
drag指令完整代码
import { App } from 'vue'; export default {
install(Vue: App<Element>) {
Vue.directive('drag', {
mounted(el: HTMLElement, bind) {
el.onmousedown = (e) => {
let elLeft = el.offsetLeft;
let elTop = el.offsetTop;
let dom = <HTMLElement>e.target;
if (dom.classList.contains('drag-target')) {
let x = e.clientX;
let y = e.clientY;
document.onmousemove = (move: MouseEvent) => {
let bodyW = document.body.clientWidth;
let bodyH = document.body.clientHeight; let left = elLeft - (x - move.clientX);
if (left < 0) {
left = 0;
}
if (left > bodyW - el.offsetWidth) {
left = bodyW - el.offsetWidth;
}
el.style.left = left + 'px'
let top = elTop - (y - move.clientY);
if (top < 0) {
top = 0;
}
if (top > bodyH - el.offsetHeight) {
top = bodyH - el.offsetHeight
}
el.style.top = top + 'px' document.onmouseup = (up: MouseEvent) => {
document.onmousemove = null;
document.onmouseup = null
}
if (window.getSelection()) {
window.getSelection()?.removeAllRanges()
}
}
}
}
},
unmounted(el, bind) {
el.onmousedown = null;
} })
}
}
使用
import DragDirective from './DragDirective' createApp(App).use(DragDirective).mount('#app')注册指令到Vue App上,然后在需要移动的html节点上加上 v-drag ,并在触发拖拽的子节点的class上,加上drag-target
<div
class="f-dialog"
v-if="show"
v-drag
ref="dialog"
:style="{ left: data.left + 'px' }"
>
<div class="f-dialog-header drag-target">
<slot name="header">
<span>{{ title }}</span>
</slot>
<f-icon
icon="icon-close"
class="f-modal-close"
@click="close(false)"
></f-icon>
</div>
<div class="f-dialog-content">
<slot></slot>
</div>
<div class="f-dialog-footer">
<slot name="footer">
<button @click="close(true)">确定</button>
<button @click="close(false)">取消</button>
</slot>
</div>
</div>
效果图

更多干货,以及本文的示例代码, 欢迎关注我的公众号: 青城同学 回复 拖拽代码 获取下载地址
当然也可以扫码

使用TypeScript给Vue 3.0写一个指令实现组件拖拽的更多相关文章
- 从0写一个Golang日志处理包
WHY 日志概述 日志几乎是每个实际的软件项目从开发到最后实际运行过程中都必不可少的东西.它对于查看代码运行流程,记录发生的事情等方面都是很重要的. 一个好的日志系统应当能准确地记录需要记录的信息,同 ...
- 每天一个JavaScript实例-html5拖拽
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- jQuery 学习笔记3 点击弹出一个div并允许拖拽移动
这里我看了下http://qings.blog.51cto.com/4857138/998878/ 的文章,感谢他的分享. 首先我们有一个a标签和一个div,div默认是不显示的,当用户点击时改为显示 ...
- 用extjs6.0写一个点击新建窗口的功能
一.写一个按钮 注意id { id: 'ListEdit', text:'编辑', iconCls:'x-fa fa-edit' } 二.写新建的页面 下面我新建的是表单,有几点需要注意的: ① 因为 ...
- Extjs6(二)——用extjs6.0写一个系统登录及注销
本文基于ext-6.0.0 一.写login页 1.在view文件夹中创建login文件夹,在login中创建文件login.js和loginController.js(login.js放在class ...
- 基于vue框架手写一个notify插件,实现通知功能
简单编写一个vue插件,当点击时触发notify插件,dom中出现相应内容并且在相应时间之后清除,我们可以在根组件中设定通知内容和延迟消失时间. 1. 基础知识 我们首先初始化一个vue项目,删除不需 ...
- Vue.2.0.5-自定义指令
简介 除了默认设置的核心指令( v-model 和 v-show ),Vue 也允许注册自定义指令.注意,在 Vue2.0 里面,代码复用的主要形式和抽象是组件--然而,有的情况下,你仍然需要对纯 D ...
- 如何写一个自己的组件库,打成NPM包,并上传到NPM远程
1.首先使用vue create my_project 构建一个自己的Vue项目 2.vue.config.js和package.json配置如下,做了些修改 const path = require ...
- vue 基础-->进阶 教程(2): 指令、组件
第二章 建议学习时间4小时 课程共3章 前面的nodejs教程并没有停止更新,因为node项目需要用vue来实现界面部分,所以先插入一个vue教程,以免不会的同学不能很好的完成项目. 本教程,将从零 ...
随机推荐
- switch-case 选择语句
0. 语句模型 Go 里的选择语句模型是这样的 switch 表达式 { case 表达式1: 代码块 case 表达式2: 代码块 case 表达式3: 代码块 case 表达式4: 代码块 cas ...
- 聊聊分布式下的WebSocket解决方案
前言 最近王子自己搭建了个项目,项目本身很简单,但是里面有使用WebSocket进行消息提醒的功能,大体情况是这样的. 发布消息者在系统中发送消息,实时的把消息推送给对应的一个部门下的所有人. 这里面 ...
- 《InnoDB存储引擎》笔记
第1章 Mysql体系结构和存储引擎 1.1 定义数据库和实例 数据库:database,物理的操作系统文件或其他形式文件类型的集合.当使用NDB存储引擎时,数据库文件可能是存放在内存中而不是磁盘之上 ...
- Python3 环境搭建 保姆式 详细教程!真手把手教学!
本文我们将向大家介绍如何在本地搭建 Python3 开发环境. Python3 可应用于多平台包括 Windows.Linux 和 Mac OS X. Unix (Solaris, Linux, Fr ...
- Apollo系列(一):分布式配置中心Apollo安装(Linux、Docker)
一.介绍 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管理 ...
- echarts中折线图切换为数据视图(表格布局)表头无法对齐解决方法
dataView: { show: true, readOnly: true, option ...
- 开源 UI 库中,唯一同时实现了大表格虚拟化和树表格的 Table 组件
背景 有这样一个需求,一位 React Suite(以下简称 rsuite)的用户,他需要一个 Table 组件能够像 Jira Portfolio 一样,支持树形数据,同时需要支持大数据渲染. 截止 ...
- vue-cli4.x+部署vue2.x开发环境 引入iview
1.首先当然是安装nodejs,因为这边我已经装过了 所以就不再安装了 直接下一步下一步完成安装. 2.get~安装完成后先测试一下是否在本地已经安装成功了,能出现下面这两个提示就说明安装已经成功了 ...
- 看动画学算法之:linkedList
目录 简介 linkedList的构建 linkedList的操作 头部插入 尾部插入 中间插入 删除节点 简介 linkedList应该是一种非常非常简单的数据结构了.节点一个一个的连接起来,就成了 ...
- python中random的一些用法
#(1)随机小数 import random print(random.random()) #随机大于0 且小于1 之间的小数 ''' 0.9441832228391154 ''' print(ra ...