使用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教程,以免不会的同学不能很好的完成项目. 本教程,将从零 ...
随机推荐
- 详尽的 Elasticsearch7.X 安装及集群搭建教程
为了更好的阅读体验,欢迎访问 原文阅读链接 简介 首先引用 Elasticsearch (下文简称 ES)官网的一段描述: Elasticsearch 是一个分布式.RESTful 风格的搜索和数据分 ...
- Redis学习(四)redis发布订阅
文章更新时间:2020/04/22 一.简介 Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息. Redis 客户端可以订阅任意数量的频道. ...
- 爬虫日志监控 -- Elastc Stack(ELK)部署
傻瓜式部署,只需替换IP与用户 导读: 现ELK四大组件分别为:Elasticsearch(核心).logstash(处理).filebeat(采集).kibana(可视化) 在elastic官网下载 ...
- Go 基本类型
整形 标准整形 标准整形是按长度进行分类的,其中uint8为byte型,int16为C语言中的short型,int64为C语言中的long类型. 类型 描述 uint8 无符号 8位整型 (0 到 2 ...
- Python-在不在判断 in 和 in判断协议- in __contains__
in 判断元素是否在序列中, 可以查看 in == is 区别 __contians__ 如果是对象则调用对象中的 __contains__方法 class BeiMenChuiXue: def __ ...
- Java学习day04
day04-课堂笔记 1.运算符[剩下的运算符] * 赋值运算符 两种类型: 基本赋值运算符:= 扩展的赋值运算符: += -= *= /= %= 它们都是先执行等号右边的表达式,最后再经过运算赋值给 ...
- 初识 Istio - 服务网格管理工具
What is a service mesh(服务网格)? 微服务在国内流行已经多年了,大多数公司选择了基于容器化技术( Docker )以及容器编排管理平台 ( Kubernetes )落地微服务 ...
- Python单向链表的实现
链表由一系列不必在内存中相连的结构构成,这些对象按线性顺序排序.每个结构含有表元素和指向后继元素的指针.最后一个单元的指针指向NULL.为了方便链表的删除与插入操作,可以为链表添加一个表头. 删除操作 ...
- Oracle 数据库导入数据和编码问题
配置 control 文件: load data characterset utf8 append into table role_res_gold fields terminated by ';' ...
- ConcurrentHashMap原理分析(二)-扩容
概述 在上一篇文章中介绍了ConcurrentHashMap的存储结构,以及put和get方法,那本篇文章就介绍一下其扩容原理.其实说到扩容,无非就是新建一个数组,然后把旧的数组中的数据拷贝到新的数组 ...