Vue提高20 日期选择器插件
以前收藏了一篇自己动手实现日期选择器的插件,最近没什么事,就想着仿照ElementUI的DatePicker,自己也写了一个简易的日期选择器,本以为不会很麻烦,实际动手才发现有很多问题需要解决。并且在写完之后,才发现可扩展性很差,距离ElementUI的水平差的很远,下一步就是把ElementUI的源码学习一下,看清楚自己的差距。
结构
我将这个日期选择做成了Vue的插件的形式,有三个文件:
- index.js
- MyDate.js
- MyDatePicker.vue
index.js
很简单,只有一个方法intstall
,在install
里面注册了全局组件:
import MyDatePicker from './MyDatePicker'export default {install(Vue) {Vue.component('MyDatePicker', MyDatePicker)}
}
MyDatePicker.vue
是UI部分,在这里定义样式和交互事件,我将数据单独放到了MyDate.js
中,以Class形式导出
数据部分
我的顺序是先完成MyDate.js
的数据部分,一个日期选择器基本结构如下:
最后导出一个二维数组,二维数组外层包含6个数组,对应日期选择器中的每一行的数据,内层数组又包含7个对象元素,对应周一到周日,这样相当于总共有42个元素,正好对应面板中的42个日期。
当选择一个日期后,首先通过new Date
构造函数获得当前日期所在月的第一天及这一天是星期几
// 当前选择日期所在月的第一天及这一天是星期几
const firstDayOfCurrentMonth = new Date(this.current.year, this.current.month - 1);
const firstDayOfWeek = firstDayOfCurrentMonth.getDay();
要注意的是,new Date().getMonth()
的范围是[0, 11]
,和我们日常使用的月份是少1
的。而我在current
里面存的日期是为了显示所用的,已经加过1
了,所以需要在上面减1
接下来,通过两层的遍历来生成我们所需要的二维数组,外层遍历是对应的是行数据:
for (let row = 0; row < 6; row++) {
}
关键是内层数据,假设我们选择的就是2019年6月,6月1日是星期六,在二维数组的内部数组里面的七个元素,应该吻别对应['日', '一', '二', '三', '四', '五', '六']
,现在6月1日星期六,它位于数组的第七项,补齐这个数组的结果应该是:
[5.26, 5.27, 5.28, 5.29, 5.30, 5.31, 6.1]
JS的Date
构造函数会自动对超出当前月份的日期进行转换,意思就是,当我们构造一个日期new Date(2019, 5, 0)
,它会自动往前倒一天,生成的日期就是2019-05-31
所以上面的数组转为对应的以6月为天数就是:
[-5, -4, -3, -2, -1, 0, 1]
所以当前遍历的范围就是[-5, 1]
,起始点与6月1日的星期几存在这样的关系:
// 内层遍历起始点
let weekLoopStart = -firstDayOfWeek + 1; // -5
结束点是7 + weekLoopStart - 1
这样当内层遍历结束一次时,将weekLoopStart
加7
,就可以生成新的一行数据了:
[2, 3 4, 5, 6, 7, 8]
所以两层遍历的基本形式就是:
// 行数据
for (let row = 0; row < 6; row++) {const rowDate = [];// 列数据for (let weekDay = weekLoopStart; weekDay <= 7 + weekLoopStart - 1; weekDay++) {// 生成需要的对象}weekLoopStart += 7;this.dates.push(rowDate)
}
有点绕,而且可扩展性也不是很好,还是太笨了。
在内层遍历生成的对象有这样几个属性:
const targetDate = new Date(this.current.year, this.current.month - 1, weekDay);
const day = targetDate.getDate();
const month = targetDate.getMonth() + 1;
rowDate.push({date: targetDate,value: format(targetDate),label: day,key: weekDay,isCurrentMonth: month === this.current.month,isToday: +targetDate === +this.today
})
date
是标准的日期对象,value
是选择后用于展示的格式化的日期,label
是在日历中选择的日期,key
是整个遍历过程中它实际的标号,isCurrent
用来判断这个日期是否属于当前月份,还是以-5
这样的格式转换为前一个月的日期(这样的日期在面板上是有不同的样式),isToday
用来标识今天的日期:
这样就生成了一个二维数组放到了this.dates
这个实例属性中
当改变选择的月份,面板的日期也会随着变化,对应的实例方法就是changeDate
,因为刚才的生成数据的getDateArray
方法都是依赖于this.current
来进行的,所以只需要改变this.current
的值,然后重新执行getDateArray
方法就行了
// 改变日期
changeDate(date = new Date()) {this.current = {year: date.getFullYear(),month: date.getMonth() + 1};this.getDateArray()
}
这样基本的数据就完成了。
UI部分
UI部分是在.vue
的单文件组件完成的,面板使用了<table>
标签,在date
里面引入MyDate
的实例,其余都声明为计算属性,与MaDate
的实例相关联,这样形成了这样的变化过程:
有三个事情需要记录一下
(1)设定单元格样式
<tr v-for="(row, rowIndex) in tbody" :key="'row-' + rowIndex"><td v-for="cell in row" :key="cell.key" @mousedown="selectDate(cell)"><span :class="tableCellClass(cell)">{{cell.label}}</span></td>
</tr>
因为单元格的原始和遍历的数据cell
有关系,如果卸载模板中会有一大堆的代码,不太直观,用计算属性生成一个对象有没有办法传入参数,所以可以用一个method
,返回一个对象传给:class
// 设定日期单元格样式
tableCellClass(cell) {return {'not-current-month': !cell.isCurrentMonth,today: cell.isToday,selected: cell.value === this.selectedDate}
},
(2)动画效果
ElementUI的动画效果是向上滑出
它是通过Vue的<transition>
组件实现的,而<transition>
是用JS实现的动画,使用了requestAnimationFrame
的API,很流畅,而且便于复用。找个时间还是要好好看一些Vue的源码,学习一下。
我使用了CSS动画来实现,当选择框出现时,添加一个类container-visible
,将原本的height
由0
改为320px
,同时将opacity
由0
改为1
,同时添加了will-change
和transform: translateZ(0)
来提升性能:
.date-container {position: absolute;left: 0;top: 46px;color: #606266;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);background: #fff;border-radius: 4px;line-height: 30px;margin: 5px 0;transition: all 0.5s ease;border: 1px solid #e4e7ed;height: 0;overflow: hidden;will-change: height;opacity: 0;transform: translateZ(0);
}
.container-visible {height: 320px;opacity: 1;
}
实现的效果还可以,但是有两个问题,一是不太好复用,而是需要改为固定的高度,如果面板高度变化,效果就有可能有偏差
(3)第三个问题是日期选择框的出现和隐藏,它具体的逻辑如下:
- 点击输入框,出现选择框
- 点击输入框和选择框之外的部分,选择框消失
- 点击输入框和选择框之内的部分,选择框不消失
- 点击选择框的快速选择月份(那几个小箭头),选择框发生相应改变,不消失
- 点击选择框的具体日期,选择框消失,选择成功
我选用的方案是使用<input>
的focus
和blur
事件,发生两个事件时,改变控制选择框是否显示的变量containerVisible
focus
没有问题,但是blur
有着比较大的问题,首先遇到的问题时,当点击选择框的按钮功能和时期时,没有触发对应的功能,选择框就消失了(以前在开发业务的时候遇到过类似的问题),这主要是因为blur
事件发生的时机:
在click
事件发生之前blur
事件就发生了,导致click
事件没有发生时,元素就隐藏了,click
事件无效。
所以像以前一样,将选择框绑定的click
事件改为了mousedown
事件,这样做的效果是,点击日期能够选择了,并且选择事件执行了,并且之后选择框失效了,这时候上面的五条逻辑满足了1/2/5,但是3/4又出问题了,点击选择框的小按钮,选择框意外消失了。
之所以这样,是因为mousedown
事件之后,blur
事件执行,导致选择框小事,我们要做的是在mousedown
之后,不触发blur
事件,所以应该使用peventDefault
方法(注意不是stopImmdeiation
,因为不是冒泡导致的),Vue中提供的修饰符是prevent
,所以在所有的mousedown
事件后面添加上修饰符prevnet
:
<button type="button" class="btn next-month-btn" @mousedown="changeMonth(1)"><span class="iconfont icon-el-icon-arrow-right"></span>
</button>
这样条件4满足了,但是3不行,所以需要在整个选择框的容器上添加一个mousedonw
事件,并且使用prevnet
修饰符,里面的点击事件只需要使用mousedown
就可以了
<div class="date-container" :class="{'container-visible': containerVisible}" @mousedown.prevent>
</div>
这样基本上就成功了,但是还是有一些小瑕疵,一个问题就是blur
事件发生的过于容易,比如我点击浏览器之外的桌面部分,blur
事件也会发生,选择框会消失,而ElementUI的并不会消失,还有就是绑定了没有必要的点击事件,不好复用,并且不知道如果同时有多个弹出框的时候还不会有其他的问题。
ElementUI是把这块单独提出了一个方法,位于element/src/utils/clickoutside.js,它对这种情况的点击事件做了统一的处理,主要的思路就是在document
绑定了统一的点击事件,通过收集此刻的弹窗元素到一个队列中,隐藏这个队列中的元素,它没有使用blur
事件,更可控,也更适合更多的元素。
优化
这个日期选择器插件的基本功能能够满足,但是如果作为ElementUI那样的轮子,还差的很多,扩展非常困难(快速选择月、年的面板我就没有做)
下一步的计划就是首先学习clickoutside
的实现,然后学习ElementUI的源码,这个计划也好久了,要尽快执行啊~
最后,代码都在这里。
Vue提高20 日期选择器插件相关推荐
- 【vue开发问题-解决方法】(五)vue Element UI 日期选择器获取日期格式问题 t.getTime is not a function
[vue开发问题-解决方法](五)vue Element UI 日期选择器获取日期格式问题 t.getTime is not a function 参考文章: (1)[vue开发问题-解决方法](五) ...
- Datepicker日期选择器插件
##Datepicker日期选择器插件 这个插件还是比较简单的,而且样式也比较漂亮,可以自定义选择年月日.年月日时分.年月.时间段选择等等. ###效果预览: #####这个插件是基于jQuery和b ...
- EXCEL 2003-2016 日期选择器插件 支持64位系统
EXCEL 2003-2016 日期选择器插件 支持64位 使用简单且漂亮的EXCEL日期选择器插件 效果如下: Download now for Excel 2007 – 2016 Download ...
- html备忘录时间提醒,jQuery简单带备忘录功能的日期选择器插件
jQuery.Simple-EventCalendar是一款带备忘录功能的jQuery日期选择器插件.你可以在某个日期上记录需要做的事情或已经做的事情,就像工作中的备忘录一样,十分方便. bower ...
- 最好用的 12 款 Vue Timepicker 时间日期选择器测评推荐 - 卡拉云
本文首发:<最好用的 12 款 Vue Timepicker 时间日期选择器测评推荐 - 卡拉云> Vue 时间日期选择器(date-timepicker)组件在使用 Vue 框架开发中使 ...
- 原生js实现日期选择器插件 简单风格日期选择插件
原生js实现日期选择插件 xs-DatePicker ** 效果图 ** ** 简介 通过原生HTML/CSS/JavaScript完成一个日期选择器. 代码如下: ** <!-- 时间:202 ...
- Vue日期选择器插件(vue-datepicker-local)
参考文档: https://github.com/weifeiyue/vue-datepicker-local 支持年份选择器,月份选择器,日期选择器,时间选择器,且均支持范围选择 效果如下演示年月日 ...
- bootstrap4日期选择器插件tempusdominus-bootstrap-4具体实例
先上图 代码 <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" lang="en&qu ...
- html时间选择控件 简书,flatpickr好看的日期选择器插件
百度盘链接: https://pan.baidu.com/s/1c2GRktU 密码: jf3v (主要需要的都在dist这个文件夹内) 1.导入日期选择器的css样式(底部有各种颜色对应的样式) d ...
- 用 Vue 实现原生日期选择器
效果: 部分代码 .vue <template><div class="date-select"><div class="date-sele ...
最新文章
- Allan方差分析方法的直观理解
- Trie树详解及其应用
- 3.11 总结-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
- Android自定义控件(三)——有弹性的ListView
- Matlab | Matlab从入门到放弃(5)——矩阵与format
- Python爬虫1-----------placekitten 入门
- 数据库面试题目经典大全
- Educational Codeforces Round 80 (Rated for Div. 2) 二分 + 状压
- 数据同步到redis中时候需要 需要给关联的表增加id 如果是一对多 则增加list存储id 如果是一个 则增加一个字段 ;目的是便于取值...
- CryptoQuant CEO:比特币大规模从Coinbase流出是最强劲的看涨信号
- 51nod 1050循环数组最大字段和
- 原生html使用element组件,使用element-ui的table组件时,渲染为html格式
- VHDL_EDA课设_八音电子琴
- 梅宏:不容错过的大数据时代_我们错过了整个网络支付领域:如何为创作者修复网络...
- Mapstruct使用介绍
- css实现固定宽高比例的div
- android 排他button,javascript排他思想
- arch使用create_ap创建wifi热点
- 【项目配置学习笔记】启动Tomcat的常出现的错误及其解决办法
- 一位软件工程师的6年总结
热门文章
- 前路钉板系统在重建胸腰段稳定性中应用 [已发表]
- 相关系数excel_给你一份数据,如何去分析?手把手教你用Excel进行数据分析
- 如何入门短视频剪辑?先要有五大思维,有想法才能有如神助
- WinForm 去掉DataGridView最后一行的空白行,删除空白行
- java发微信字体颜色_java微信公众号发送消息模板
- Excel,WPS如何同时冻结行与列
- [NOI 2014]起床困难综合症
- MSM8953 Android9.0 配置USB2.0 Camera
- 微信如何html动图文件,微信里的gif动态图片如何导出保存出来?看完本文你就知道了...
- 【学习记录】SLAM线特征基础:LSD算法、LBD描述子、普朗克坐标、EDLines算法