Vue拖拽组件开发实例
vue是一套用于构建用户界面的渐进式框架。可以用他来封装单文件组件来开发更为复杂的单页应用。 本文主要是通过封装一个拖拽组件的例子,来分析Vue组件化相关知识。
为什么选择Vue?
主要原因:对于前端开发来说,兼容性是我们必须要考虑的问题之一。我们的项目不需要兼容低版本浏览器。项目本身也是一个数据驱动型的。加之,Vue本身具有以下主要特性:
- 使用虚拟DOM;
- 轻量级框架;
- 高效的数据绑定;
- 灵活的组件系统;
- 完整的开发生态链。
这就是我们为什么选择Vue框架的一些原因。
为什么要封装成一个Vue组件?
主要目的是可提高代码的复用性和可维护性。
- 复用性:组件化后,一些样式和逻辑均通过配置参数的方式去差异化体现,所以参数的可配置性提高了组件的复用率和灵活性。
- 可维护性:组件化后,组件内部的逻辑只对组件负责,外部的逻辑只通过配置参数适配,所以提高了代码的逻辑清晰度,可以快速定位代码出现问题的地方。
组件化搭建页面图示:
上图可看出,在Vue中,所谓组件化搭建页面,简单来说,页面实际上是由一个个功能独立的组件搭建而成。这些组件之间可以组合、嵌套,最终形成了我们的页面。
组件构成
下面是一个完成的组件构成:
// 组件内模板<template></template>// 组件内逻辑代码<script type="text/javascript"></script>// 组件内封装的样式<style lang="scss" scoped></style>
开发Vue移动拖拽组件为例
拖拽原理
手指在移动的过程中,实时改变元素的位置即top和left值,使元素随着手指的移动而移动。
拖拽实现
- 始拖动时:获取到接触点相对于整个视图区的坐标
clientX,clientY
;获取元素距离视图上侧和左侧的距离initTop
,initLeft
;计算接触点距离元素上侧和左侧的距离elTop=clientY-initTop
,elLeft=clientX-initLeft
; - 拖动过程中:通过
currTop=clientY-elTop
,currLeft=clientX-elLeft
实时获取元素距离视图上侧和左侧的距离值,并赋值给元素,使元素跟着手指的移动而动起来; - 拖动结束,定位元素。
Vue中的实现
使用Vue,最大的不同之处是我们几乎不去操作DOM,要充分利用Vue的数据驱动来实现拖拽功能。本例中,我们只需在垂直方向上拖动元素,所以只需考虑垂直方向的移动即可。
上图中,通过data中的dragList渲染拖拽区域列表,代码如下:
template:<div class="drag-title">拖拽可调整顺序</div><ul class="drag-list"> <li class="drag-item" v-for="(item,index) in dragList"> {{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的结构做一下修改,代码如下:
<li class="drag-item" v-for="(item,index) in dragList" @touchstart="touchStart" @touchmove="touchMove(index, item, $event)" @touchend="touchEnd"> <div class="leave-block" v-show="item.isShowUp"></div> // 向上拖拽时留空 <div class="">{{item.txt}}</div> <div v-show="item.isShow" class="leave-block"></div> // 向下拖拽时留空 </li>
- 拖拽开始:将元素的定位方式由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实现移动端拖拽组件的过程。我们知道,有些项目是需要在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数据驱动的核心思想。
- 函数式编程很难,这正是你要学习它的原因
- Scrapy源码(1)——爬虫流程概览
- Scrapy-Redis分布式爬虫源码解析
- scala 隐式详解(implicit关键字)
- scala的option和some
- PEP8规则及Pycharm应用
- (四) 如何将socket设置为非阻塞模式
- Scala Collections集合的几个重要概念
- (五)如何编写高性能日志
- Scala之偏函数Partial Function
- (六)关于网络编程的一些实用技巧和细节
- 快学Scala习题答案汇总
- (八)高性能服务器架构设计总结1——以flamigo服务器代码为例
- (八)高性能服务器架构设计总结2——以flamigo服务器代码为例
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 求求你,不要再纠结指针了(1) ——万能转化公式
- 求求你,不要再纠结指针了(2)——函数指针
- 用Python解决100个问题 | 倒计时
- 【转载】【ionic+angularjs】angularjs ui-router路由简介
- 实时性迷思(1) —— “快是优点么?”
- Java中Thread的join方法为什么能让线程插队?
- 再见 ELK,是时候拥抱下一代日志系统 Loki 了
- 利用VBAProject来共用VBA代码
- 线上频出MySQL死锁问题!分享一下自己教科书般的排查和分析过程!
- 换人!这些算法都不会还学什么操作系统
- 【赵渝强老师】第一个Oracle的手工备份和恢复
- LeetCode刷题DAY 36:最小路径和
- Java底层-JMX
- VBA汇总多个Sheet数据
- VBA数据类型String