Vue拖拽组件开发实例详解


Posted in Javascript onMay 11, 2018

为什么选择Vue?

主要原因:对于前端开发来说,兼容性是我们必须要考虑的问题之一。我们的项目不需要兼容低版本浏览器。项目本身也是一个数据驱动型的。加之,Vue本身具有以下主要特性:

•使用虚拟DOM;
•轻量级框架;
•高效的数据绑定;
•灵活的组件系统;
•完整的开发生态链。

这就是我们为什么选择Vue框架的一些原因。

为什么要封装成一个Vue组件?

主要目的是可提高代码的复用性和可维护性。

•复用性:组件化后,一些样式和逻辑均通过配置参数的方式去差异化体现,所以参数的可配置性提高了组件的复用率和灵活性。

•可维护性:组件化后,组件内部的逻辑只对组件负责,外部的逻辑只通过配置参数适配,所以提高了代码的逻辑清晰度,可以快速定位代码出现问题的地方。

组件化搭建页面图示:

Vue拖拽组件开发实例详解

上图可看出,在Vue中,所谓组件化搭建页面,简单来说,页面实际上是由一个个功能独立的组件搭建而成。这些组件之间可以组合、嵌套,最终形成了我们的页面。

组件构成

下面是一个完成的组件构成:

// 组件内模板
// 组件内逻辑代码
<script type="text/javascript"></script>
// 组件内封装的样式
<style lang="scss" scoped></style>

开发Vue移动拖拽组件为例

拖拽原理

手指在移动的过程中,实时改变元素的位置即top和left值,使元素随着手指的移动而移动。

拖拽实现

Vue拖拽组件开发实例详解

•开始拖动时:获取到接触点相对于整个视图区的坐标clientX,clientY;获取元素距离视图上侧和左侧的距离 initTop,initLeft;计算接触点距离元素上侧和左侧的距离 elTop = clientY - initTop, elLeft = clientX - initLeft;
•拖动过程中:通过 currTop = clientY - elTop, currLeft = clientX - elLeft实时获取元素距离视图上侧和左侧的距离值,并赋值给元素,使元素跟着手指的移动而动起来;
•拖动结束,定位元素。

Vue中的实现

使用Vue,最大的不同之处是我们几乎不去操作DOM,要充分利用Vue的数据驱动来实现拖拽功能。本例中,我们只需在垂直方向上拖动元素,所以只需考虑垂直方向的移动即可。

Vue拖拽组件开发实例详解

上图中,通过data中的dragList渲染拖拽区域列表,代码如下:

template:
<div class="drag-title">拖拽可调整顺序</div>
<ul class="drag-list">
 <li class="drag-item">{{item.txt}}</li>
</ul>
script:
 
export default {
data() {
return {
dragList:null
}
},
created() {
this.dragList = [
{
isDrag: false,
txt: '列表1',
isShow: false
}
...
]
},
}

假设我们将元素从位置1拖至位置3,本质上是数组的顺序发生了改变。这就有必要提一下Vue的最大特性:数据驱动。

所谓的数据驱动就是当数据发生变化时,通过修改数据状态,使用户界面发生相应的改变,开发者不需要手动的去修改DOM。

Vue的数据驱动是通过MVVM这种框架来实现的,MVVM框架主要包含3个部分:Model、View、Viewmodel。

? Model:数据部分;
? View:视图部分;
? Viewmodel:连接视图与数据的中间件。

顺着这个思路走下去,我们知道:

? oldIndex:元素在数组中的初始索引index;
? elHeight:单个元素块的高;
? currTop = clientY - elTop:元素在拖动过程中距离可视区上侧距离;
? currTop - initTop > 0:得知元素是向上拖拽;
? currTop - initTop < 0:得知元素是向下拖拽。

我们以向下拖拽来说:

? 首先,我们要在拖拽结束事件touchend中判断元素从拖动开始到拖动结束时拖动的距离。若小于某个设定的值,则什么也不做;
? 然后,在touchmove事件中判断,若(currTop - initTop) % elHeight>= elHeight/2成立,即当元素拖至另一个元素块等于或超过1/2的位置时,即可将元素插入到最新的位置为newIndex = (currTop - initTop) / elHeight + oldIndex。
? 最后,若手指离开元素,那么我们在touchend事件中,通过this.dragList.splice(oldIndex, 1),this.dragList.splice(newIndex, 0, item)重新调整数组顺序。页面会根据最新的dragList渲染列表。

写到这里,我们俨然已经用Vue实现了移动端的拖拽功能。但是拖拽体验并不好,接下来,我们对它进行优化。

优化点:我们希望,在元素即将可能落到的位置,提前留出一个可以放得下元素的区域,让用户更好的感知拖拽的灵活性。

方案:(方案已被验证是可行的)将li的结构做一下修改,代码如下:

<ul>
 <li class="drag-item">
<div class="leave-block"></div>
// 向上拖拽时留空
<div class="">{{item.txt}}</div>
<div class="leave-block"></div>
// 向下拖拽时留空</li>
</ul>

•拖拽开始:将元素的定位方式由static设置为absolute,z-index设置为一个较大的值,防止元素二次拖拽无效;

•拖拽过程中:将元素即将落入新位置的那个li下div的item.isShow设置为true,其他li下div的item.isShow均设置为false;

•拖拽结束:将所有li下div的item.isShow 均设置为false,将元素定位方式由absolute设置为static。

贴一段伪代码:

touchStart(e){
// 获取元素距离视口顶部的初始距离
initTop = e.currentTarget.offsetTop;
// 开始拖动时,获取鼠标距离视口顶部的距离
initClientY = e.touches[0].clientY;
// 计算出接触点距离元素顶部的距离
elTop = e.touches[0].clientY - initTop;
},
touchMove(index, item, e){
// 将拖拽结束时,给元素设置的static定位方式移除,防止元素二次拖拽无效
e.target.classList.remove('static');
// 给拖拽的元素设置绝对定位方式
e.target.classList.add('ab');
// 获取元素在拖拽过程中距离视口顶部距离
currTop = e.touches[0].clientY - elTop;
// 元素在拖拽过程中距离视口顶部距离赋给元素
e.target.style.top = currTop ;
// 获取元素初始位置
oldIndex = index;
// 获取拖拽元素
currItem = item;
// 若元素已经拖至区域外
if(e.touches[0].clientY > (this.dragList.length) * elHeight){
// 将元素距离上侧的距离设置为拖动区视图的高
currTop = (this.dragList.length) * elHeight;
return;
}
// 向下拖拽
if(currTop > initTop ){
// 若拖拽到大于等于元素的一半时,即可将元素插入到最新的位置
if((currTop - initTop) % elHeight>= elHeight / 2){
// 计算出元素拖到的最新位置
newIndex = Math.round((currTop - initTop) / elHeight) + index;
// 确保新元素的索引不能大于等于列表的长度
if(newIndex < this.dragList.length){
// 将所有列表留空处隐藏
for(var i = 0;i< this.dragList.length;i++){
this.dragList[i].isShow = false;
}
// 将元素即将拖到的新位置的留空展示
this.dragList[newIndex].isShow = true;
}
else {
return;
}
}
}
// 向上拖拽,原理同上
if(currTop < initTop){ ... } }, touchEnd(e){ // 若拖动距离大于某个设定的值,则按照上述,执行相关代码 if(Math.abs(e.changedTouches[0].clientY - initClientY ) > customVal){
this.dragList.splice(oldIndex, 1);
this.dragList.splice(newIndex, 0, currItem);
for(var i = 0;i< this.dragList.length;i++){
this.dragList[i].isShow = false;
this.dragList[i].isShowUp = false;
}
}
e.target.classList.remove('ab');
e.target.classList.add('static');
}

优化后,如下图所示:

Vue拖拽组件开发实例详解

以上便是用Vue实现移动端拖拽组件的过程。我们知道,有些项目是需要在PC端用Vue实现此功能。这里简单提一下PC与移动端的区别如下:

•PC端可以使用的事件组有两种:第一种:H5新特性draggable,dragstart,drag,dragend;第二种:mousedown,mousemove,mouseup;

•PC端获取鼠标坐标是通过e.clientX,clientY,区别于移动端的e.touches[0].clientX,e.touches[0].clientY。

小结

本文从Vue拖拽组件开发为例,剖析Vue组件的结构、开发思路、Vue的数据驱动等,对Vue组件化的原理,进行了更深入的理解。 并将Vue实现拖拽的方案提供给大家学习研究。

P.S. 牢记一点,切勿在Vue中过多得操作DOM,要能深入理解Vue数据驱动的核心思想。

Javascript 相关文章推荐
30个精美的jQuery幻灯片效果插件和教程
Aug 23 Javascript
iframe的父子窗口之间的对象相互调用基本用法
Sep 03 Javascript
简化版手机端照片预览组件
Apr 13 Javascript
jquery序列化方法实例分析
Jun 10 Javascript
jQuery 3 中的新增功能汇总介绍
Jun 12 Javascript
vue实现ajax滚动下拉加载,同时具有loading效果(推荐)
Jan 11 Javascript
vue 父组件给子组件传值子组件给父组件传值的实例代码
Apr 15 Javascript
vue-cli的build的文件夹下没有dev-server.js文件配置mock数据的方法
Apr 17 Javascript
Vue两种组件类型:递归组件和动态组件的用法
Aug 06 Javascript
基于JavaScript实现简单抽奖功能代码实例
Oct 20 Javascript
Vue实现随机验证码功能
Dec 29 Vue.js
js面向对象编程OOP及函数式编程FP区别
Jul 07 Javascript
node前端模板引擎Jade之标签的基本写法
May 11 #Javascript
JS实现的简单下拉框联动功能示例
May 11 #Javascript
JS实现table表格内针对某列内容进行即时搜索筛选功能
May 11 #Javascript
node前端开发模板引擎Jade的入门
May 11 #Javascript
Node.js 使用jade模板引擎的示例
May 11 #Javascript
Node.js Express安装与使用教程
May 11 #Javascript
Node.js创建HTTP文件服务器的使用示例
May 11 #Javascript
You might like
Centos PHP 扩展Xchche的安装教程
2016/07/09 PHP
PHP基于session.upload_progress 实现文件上传进度显示功能详解
2019/08/09 PHP
Javascript 验证上传图片大小[客户端]
2009/08/01 Javascript
jQuery模拟超链接点击效果代码
2013/04/21 Javascript
js获取当前页面路径示例讲解
2014/01/08 Javascript
使用JS取得焦点(focus)元素代码
2014/03/22 Javascript
javascript实现表单验证
2016/01/29 Javascript
基于JS实现无缝滚动思路及代码分享
2016/06/07 Javascript
Node.js 文件夹目录结构创建实例代码
2016/07/08 Javascript
Bootstrap Modal遮罩弹出层(完整版)
2016/11/21 Javascript
AngularJS的Filter的示例详解
2017/03/07 Javascript
js判断PC端与移动端跳转
2020/12/24 Javascript
Vue中计算属性computed的示例解读
2017/07/26 Javascript
AngularJS发送异步Get/Post请求方法
2018/08/13 Javascript
[01:05:52]DOTA2-DPC中国联赛 正赛 Ehome vs Aster BO3 第一场 2月2日
2021/03/11 DOTA
Python实现扫描指定目录下的子目录及文件的方法
2014/07/16 Python
Python3.2中Print函数用法实例详解
2015/05/19 Python
使用python对excle和json互相转换的示例
2018/10/23 Python
PyGame贪吃蛇的实现代码示例
2018/11/21 Python
Linux上使用Python统计每天的键盘输入次数
2019/04/17 Python
python 实现批量图片识别并翻译
2020/11/02 Python
python 下载m3u8视频的示例代码
2020/11/11 Python
什么是CSS3 HSLA色彩模式?HSLA模拟渐变色条
2016/04/26 HTML / CSS
HTML5 visibilityState属性详细介绍和使用实例
2014/05/03 HTML / CSS
HTML5 Canvas 旋转风车绘制
2017/08/18 HTML / CSS
国际贸易个人求职信范文
2014/01/04 职场文书
档案接收函范文
2014/01/10 职场文书
诚信考试承诺书范文
2015/04/29 职场文书
2015年生活老师工作总结
2015/05/27 职场文书
党员干部学习十八届五中全会精神心得体会
2016/01/05 职场文书
小程序教您怎样你零成本推广获取数万用户的方法
2019/07/30 职场文书
Redis安装启动及常见数据类型
2021/04/14 Redis
Python爬虫之爬取二手房信息
2021/04/27 Python
python 如何用terminal输入参数
2021/05/25 Python
从QQtabBar看css命名规范BEM的详细介绍
2021/08/07 HTML / CSS
tomcat正常启动但网页却无法访问的几种解决方法
2022/05/06 Servers