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 相关文章推荐
firefox和IE系列的相关区别整理 以备后用
Dec 28 Javascript
5秒后跳转效果(setInterval/SetTimeOut)
May 03 Javascript
Extjs4中tree的拖拽功能(可以两棵树之间拖拽) 简单实例
Dec 08 Javascript
jQuery实现MSN中文网滑动Tab菜单效果代码
Sep 09 Javascript
微信小程序实战之轮播图(3)
Apr 17 Javascript
微信小程序中做用户登录与登录态维护的实现详解
May 17 Javascript
ES6 中可以提升幸福度的小功能
Aug 06 Javascript
微信小程序使用swiper组件实现类3D轮播图
Aug 29 Javascript
vue axios封装及API统一管理的方法
Apr 18 Javascript
vue 框架下自定义滚动条(easyscroll)实现方法
Aug 29 Javascript
使用 Github Actions 自动部署 Angular 应用到 Github Pages的方法
Jul 20 Javascript
ant design vue导航菜单与路由配置操作
Oct 28 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
基于curl数据采集之正则处理函数get_matches的使用
2013/04/28 PHP
CI分页类首页、尾页不显示的解决方法
2016/03/28 PHP
PHP迭代与递归实现无限级分类
2017/08/28 PHP
php微信公众号开发之图片回复
2018/10/20 PHP
一次因composer错误使用引发的问题与解决
2019/03/06 PHP
js压缩工具 yuicompressor 使用教程
2010/03/31 Javascript
JQuery学习笔记 nt-child的使用
2011/01/17 Javascript
js限制文本框输入长度两种限制方式(长度、字节数)
2012/12/19 Javascript
查看图片(前进后退)功能实现js代码
2013/04/24 Javascript
登陆成功后自动计算秒数执行跳转
2014/01/23 Javascript
js实现编辑div节点名称的方法
2014/12/17 Javascript
js+css实现导航效果实例
2015/02/10 Javascript
jQuery实现自动与手动切换的滚动新闻特效代码分享
2015/08/27 Javascript
单行 JS 实现移动端金钱格式的输入规则
2017/05/22 Javascript
史上最全JavaScript数组去重的十种方法(推荐)
2017/08/17 Javascript
js Element Traversal规范中的元素遍历方法
2018/04/19 Javascript
页面点击小红心js实现代码
2018/05/26 Javascript
angularJs select绑定的model取不到值的解决方法
2018/10/08 Javascript
JS+CSS实现过渡特效
2021/01/02 Javascript
python抓取豆瓣图片并自动保存示例学习
2014/01/10 Python
python解析xml文件操作实例
2014/10/05 Python
浅析Python中的赋值和深浅拷贝
2017/08/15 Python
python中的迭代和可迭代对象代码示例
2017/12/27 Python
python基于pdfminer库提取pdf文字代码实例
2019/08/15 Python
python 队列基本定义与使用方法【初始化、赋值、判断等】
2019/10/24 Python
pytorch常见的Tensor类型详解
2020/01/15 Python
使用Python+selenium实现第一个自动化测试脚本
2020/03/17 Python
露营世界:Camping World
2017/02/02 全球购物
某公司部分笔试题
2013/11/05 面试题
《宿建德江》教学反思
2014/04/23 职场文书
公司节能减排倡议书
2014/05/14 职场文书
大学生学雷锋活动总结
2014/06/26 职场文书
小学生推普周国旗下讲话稿
2014/09/21 职场文书
个人先进事迹材料范文
2014/12/29 职场文书
考教师资格证不要错过的4个最佳时机
2019/07/17 职场文书
Pycharm连接远程服务器并远程调试的全过程
2021/06/24 Python