使用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教程,以免不会的同学不能很好的完成项目. 本教程,将从零 ...
随机推荐
- 快速了解前端开发HTML的正确姿势
摘要:web前端开发(也称为客户端开发)主要是通过html,CSS和JavaScript等前端技术,实现网站在客服端的正确显示及交互功能. 一.web标准介绍 web标准: w3c:万维网联盟组织,用 ...
- VUE开发(二)nginx配合vue来实现前后端分离部署
一.引言 由于本地是采用vue+spring boot实现的前后端分离项目,本机启动的时候先启动后场服务,再单独启动vue工程,然后可以实现全流程贯穿,但是我们要部署到服务器上的时候,一般都是打一个j ...
- linux学习(一)认识阿里云
一.简介 阿里云,全球领先的云计算服务平台,阿里巴巴集团旗下公司.致力于打造公共.开放的云计算服务平台.提供云服务器ECS.关系型数据库服务RDS.开放存储服务OSS.内容分发网络CDN.对象存储OS ...
- Tensorflow图级别随机数设置-tf.set_random_seed(seed)
tf.set_random_seed(seed) 可使得所有会话中op产生的随机序列是相等可重复的. 例如: tf.set_random_seed(1234) a = tf.random_unifor ...
- vue学习09 图片切换
目录 vue学习09 图片切换 定义图片数组:imgList:[],列表数据使用数组保存 添加图片索引:index 绑定src属性:使用v-bind,v-bind指令可以设置元素属性,比如src 图片 ...
- Centos-检查文件系统并尝试修复-fsck
fsck 检查文件系统并尝试修改错误,修复对象为设备,本质上是调用 /sbin/fsck.filesystemName 命令, filesystemName是指定设备的文件系统类型,如图分区中有文件丢 ...
- Centos-zip压缩-文件或目录-zip unzip
zip uzip 将一般文件或者目录进行压缩或者解压,默认以 .zip为后缀名 zip 相关选项 -r 递归压缩目录 -d 从压缩包中删除指定文件 -i 压缩指定文件列表文件 -x 压缩排除指定文件 ...
- Linux系统编程—有名管道
▋****1. 管道的概念 管道,又名「无名管理」,或「匿名管道」,管道是一种非常基本,也是使用非常频繁的IPC方式. 1.1 管道本质 管道的本质也是一种文件,不过是伪文件,实际上是一块内核缓冲区, ...
- 万万没想到!ModelArts与AppCube组CP了
摘要:嘘,华为云内部都不知道的秘密玩法,我悄悄告诉您! 双"魔"合璧庆双节 ↑开局一张图,故事全靠编 华为云的一站式开发平台ModelArts和应用魔方AppCube居然能玩到一起 ...
- 从字节码层次看i++和++i
关于的Java的i++和++i的区别,初学者可能会混淆,这时候有经验的同学或同事就会告诉你,++在后,就会立马加值, ++在后则会等会儿再加,所以如果i == 0 ,那么i++ == 0,++i == ...