基于Vue 撸一个指令实现拖拽功能


Posted in Javascript onOctober 09, 2019

之前撸了一个copy 指令,这次再撸一个拖拽指令。。

具体是个什么蛇皮玩意儿呢,大概就像介样:

基于Vue 撸一个指令实现拖拽功能 

emmm。。没错,看起来就是如此的鸡肋,但是莫得办法,大佬喜欢啊。

由于我们项目中用的是 element-ui ,所有这个指令只针对 element-ui 的对话框组件哈,如果你们用的别的 ui 库也有这个需求的,涂涂改改应该也能用。。

其实这个拖拽的原理还是很简单的:

1.首先鼠标按下( onmousedown )

  • 记录目标元素当前的 left 和 top 值

2.鼠标移动( onmousemove )

  • 计算每次移动的横向距离 ( disX ) 和纵向距离 ( disY )
  • 并改变元素的 left ( left = left + disX )和 top ( top = top + disY )值

3.鼠标松开( onmouseup )

完成一次拖拽,做一些收尾工作

  • left 和 top 值容易获取,关键是 disX 和 disY 怎么计算呢?

容我先普及一哈:

  • clientX :表示鼠标当前的 X 坐标
  • clientY :表示鼠标当前的 Y 坐标

那么伪代码就是:

  • disX = 鼠标按下时的 clientX - 鼠标松开时的 clientX
  • disY = 鼠标按下时的 clientY - 鼠标松开时的 clientY

就这么简单,好了,下面就开始撸代码了。

// 这个助手方法下面会用到,用来获取 css 相关属性值
const getAttr = (obj, key) => (
  obj.currentStyle
  ? obj.currentStyle[key]
  : window.getComputedStyle(obj, false)[key]
);
const vDrag = {
  inserted(el) {
   /**
    * 这里是跟据 dialog 组件的 dom 结构来写的
    * target: dialog 组件的容器元素
    * header:dialog 组件的头部区域,也是就是拖拽的区域
    */
    const target = el.children[0];
    const header = target.children[0];
    // 鼠标手型
    header.style.cursor = 'move';
    header.onmousedown = (e) => {
      // 记录按下时鼠标的坐标和目标元素的 left、top 值
      const currentX = e.clientX;
      const currentY = e.clientY
      const left = parseInt(getAttr(target, 'left'));
      const top = parseInt(getAttr(target, 'top'));
      document.onmousemove = (event) => {
        // 鼠标移动时计算每次移动的距离,并改变拖拽元素的定位
        const disX = event.clientX - currentX;
        const disY = event.clientY - currentY;
        target.style.left = `${left + disX}px`;
        target.style.top = `${top + disY}px`;
        // 阻止事件的默认行为,可以解决选中文本的时候拖不动
        return false;
      }
      // 鼠标松开时,拖拽结束
      document.onmouseup = () => {
        document.onmousemove = null;
        document.onmouseup = null;
      };
    }
  },
  // 每次重新打开 dialog 时,要将其还原
  update(el) {
    const target = el.children[0];
    target.style.left = '';
    target.style.top = '';
  },
  // 最后卸载时,清除事件绑定
  unbind(el) {
    const header = el.children[0].children[0];
    header.onmousedown = null;
  },
};
export default vDrap;

这样就实现了 最简单 的拖拽了,这样就 ok 了吗? 当然不是,这样会有什么问题呢?就是如果用力过猛把整个弹框都拖到可视区域之外了,那就抠不出来了。

所以还得完善一下,判断四个方向的边界,如果超过边界值就不动了。边界值实际上就是在屏幕上能拖动的最大距离也就是 disX 和 disY 的最大值

  • 上边界: target.offsetTop
  • offsetTop :这里可以表示目标元素( target )上边框距离页面顶部的距离
  • 下边界: body.height - target.offsetTop - header.height
  • header.height :预留高度,表示往下可以拖到只留下可拖拽区域在外面
  • 左边界: target.offsetLeft + target.width - 50
  • offsetLeft :这里可以表示目标元素左边框距离页面左边的距离
  • 50 :表示预留的宽度,可以自己随便定只要大于 0 即可,表示往左再怎么拖也会留下 50px 的宽度在外面
  • 右边界: body.width - target.offsetLeft - 50

这里 50 同上,表示往左再怎么拖也会留下 50px 的宽度在外面

这里计算边界值的方法有多种,大家可以去尝试自己的想法。然后我粗略的画了一个图,帮助理解,虽然感觉只有我自己看得懂。哈哈。。。

基于Vue 撸一个指令实现拖拽功能 

下面用代码实现边界判断就 ok了

// ...
// 以上代码省略
header.onmousedown = (e) => {
  // ...
  // 以上代码省略
  // 分别计算四个方向的边界值
  const minLeft = target.offsetLeft + parseInt(getAttr(target, 'width')) - 50;
  const maxLeft = parseInt(getAttr(document.body, 'width')) - target.offsetLeft - 50;
  const minTop = target.offsetTop;
  const maxTop = parseInt(getAttr(document.body, 'height'))
   - target.offsetTop - parseInt(getAttr(header, 'height'));
  document.onmousemove = (event) => {
    // 鼠标移动时计算每次移动的距离,并改变拖拽元素的定位
    const disX = event.clientX - currentX;
    const disY = event.clientY - currentY;
    // 判断左、右边界
    if (disX < 0 && disX <= -minLeft) {
     target.style.left = `${left - minLeft)}px`;
    } else if (disX > 0 && disX >= maxLeft) {
     target.style.left = `${left + maxLeft}px`;
    } else {
     target.style.left = `${left + disX}px`;
    }
    // 判断上、下边界
    if (disY < 0 && disY <= -minTop) {
     target.style.top = `${top - minTop)}px`;
    } else if (disY > 0 && disY >= maxTop) {
     target.style.top = `${top + maxTop}px`;
    } else {
     target.style.top = `${top + disY}px`;
    }
    return false;
  };
}

这样注册之后就可以使用了:

<el-dialog v-drag title="对话框" :visible.sync="dialogVisible"></el-dialog>

总结

以上所述是小编给大家介绍的Vue 指令实现拖拽功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
select、radio表单回显功能实现避免使用jquery载入赋值
Jun 08 Javascript
javascript静态页面传值的三种方法分享
Nov 12 Javascript
js的window.showModalDialog及window.open用法实例分析
Jan 29 Javascript
js的flv视频播放器插件使用方法
Jun 23 Javascript
javascript编写贪吃蛇游戏
Jul 07 Javascript
JS+CSS实现分类动态选择及移动功能效果代码
Oct 19 Javascript
Angular1.x复杂指令实例详解
Mar 01 Javascript
详解微信小程序中的页面代码中的模板的封装
Oct 12 Javascript
js时间戳与日期格式之间相互转换
Dec 11 Javascript
小程序自定义日历效果
Dec 29 Javascript
微信小程序自定义导航栏
Dec 31 Javascript
vue 搭建后台系统模块化开发详解
May 01 Javascript
解决Vue动态加载本地图片问题
Oct 09 #Javascript
Vue3 中的数据侦测的实现
Oct 09 #Javascript
vue3实现v-model原理详解
Oct 09 #Javascript
bootstrap+spring boot实现面包屑导航功能(前端代码)
Oct 09 #Javascript
使用Webpack提升Vue.js应用程序的4种方法(翻译)
Oct 09 #Javascript
微信小程序本地存储实现每日签到、连续签到功能
Oct 09 #Javascript
Vue.js实现大转盘抽奖总结及实现思路
Oct 09 #Javascript
You might like
Php Cookie的一个使用注意点
2008/11/08 PHP
php实现格式化多行文本为Js可用格式
2015/04/15 PHP
PHP文字转图片功能原理与实现方法分析
2017/08/31 PHP
PHP共享内存使用与信号控制实例分析
2018/05/09 PHP
thinkphp5框架调用其它控制器方法 实现自定义跳转界面功能示例
2019/07/03 PHP
jquery获取ASP.NET服务器端控件dropdownlist和radiobuttonlist生成客户端HTML标签后的value和text值
2010/06/28 Javascript
Javascript跨域请求的4种解决方式
2013/03/17 Javascript
Javascript Objects详解
2014/09/04 Javascript
深入理解JavaScript系列(28):设计模式之工厂模式详解
2015/03/03 Javascript
js简单的点击返回顶部效果实现方法
2015/04/10 Javascript
Javascript中的作用域和上下文深入理解
2015/07/03 Javascript
jQuery中的AjaxSubmit使用讲解
2016/09/25 Javascript
微信小程序实战之运维小项目
2017/01/17 Javascript
重新理解JavaScript的六种继承方式
2017/03/24 Javascript
搭建一个nodejs脚手架的方法步骤
2019/06/28 NodeJs
微信小程序事件 bindtap bindinput代码实例
2019/08/26 Javascript
LayUI switch 开关监听 获取属性值、更改状态的方法
2019/09/21 Javascript
python 解析XML python模块xml.dom解析xml实例代码
2014/02/07 Python
Python脚本处理空格的方法
2016/08/08 Python
一个基于flask的web应用诞生(1)
2017/04/11 Python
python学习教程之Numpy和Pandas的使用
2017/09/11 Python
浅谈python中对于json写入txt文件的编码问题
2018/06/07 Python
Python OpenCV利用笔记本摄像头实现人脸检测
2020/08/20 Python
实现Python3数组旋转的3种算法实例
2020/09/16 Python
CSS 说明横向进度条最后显示文字的实现代码
2020/11/10 HTML / CSS
Nike法国官方网站:Nike.com FR
2018/07/22 全球购物
优秀毕业大学生推荐信
2013/11/13 职场文书
博士研究生自我鉴定范文
2013/12/04 职场文书
档案室主任岗位职责
2014/02/12 职场文书
奉献家乡演讲稿
2014/09/13 职场文书
2015年妇幼卫生工作总结
2015/05/23 职场文书
预备党员党支部意见
2015/06/02 职场文书
运动员加油词
2015/07/18 职场文书
小学安全教育主题班会
2015/08/12 职场文书
Nginx虚拟主机的搭建的实现步骤
2022/01/18 Servers
css3 文字断裂效果
2022/04/22 HTML / CSS