博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于Vue开发一个日历组件
阅读量:6504 次
发布时间:2019-06-24

本文共 4240 字,大约阅读时间需要 14 分钟。

最近在做一个类似课程表的需求,需要自制一个日历来支持功能及展现,就顺便研究一下应该怎么开发日历组件。

更新

  • 2.23修复了2026年2月份会渲染多一行的bug,谢谢@深蓝一人童鞋提出的bug,解决方案是给二月份的日历做特殊处理,new Date(year, month+1, 0).getDay() === 6时不会再渲染后面的日期。
  • 下班更新一哈,更科学的逻辑
    // if (total_calendar_list.length > 35) {
    // nextNum = 42 - total_calendar_list.length;// } else {
    // nextNum = 35 - total_calendar_list.length;// }// if (month === 1 && new Date(year, month, 0).getDay() === 6) {
    // nextNum = 0// }nextNum = 6 - new Date(year, month+1, 0).getDay()复制代码

本文主要涉及以下内容:

  • 怎么开发一套日历皮肤?
  • 怎么计算年月日?
  • 怎么开发日历相关的功能?
  • 总结&DEMO源码

怎么开发一套日历皮肤?

层层分离,块块独立

在梳理日历逻辑之前我想先记录一下日历样式相关的问题:

下面是借鉴px2rem模式,写的基于vw为主单位的自适应转化。简单来说,就是在我们的设计稿是iPhone8一倍图的情况下,计算出某元素宽度与375(iPhone8最大宽度)的比例再与100vw相乘就得到了,该元素的vw值。因为vw是相对于屏幕的百分比单位,所以就能达到我们想要的自适应效果啦,不同的屏幕里,同一元素的展现比例是一致的。

// 借鉴了Rem布局@function pxWithVw($n) {  @return 100vw * $n / 375;}// 规定极限宽度,避免PC上观感太差@function pxWithVwMax($n) {  @return 480px * $n / 375;}复制代码

有了上面这段SCSS的函数,我们就基本可以不用考虑屏幕适配的问题了,可以尽情的敲样式啦。关于日历的样式,其实说复杂也还好,我们只需要在做之前好好的分一下层级就好了。

如同上图,每一个框表示一层元素,最后会有这样的布局--

复制代码

也许大家看完之后比较奇怪calendar__main里面的布局,为什么没有把固定的展示头分离开来,当你实际写到这里的时候会发现其实没有这个必要。

因为我们用了pxWithVw去规定calendar__main的宽度以及每个block的宽度,也就确保了每7块元素必定会占满我们一行,再利用justify-content: space-around确保我们每块元素的间隙一致即可。

好了,层层分离说完了,什么是块块独立呢?

主要指的是日期的展示块,我们是每块独立的,这样在我们渲染的时候可以很方便的决定应该以什么样式去展示他,或是应该给他绑定怎么样的事件,给我们精密控制每个日期的展示提供了便利。

怎么计算年月日?

本小节的内容总结起来其实就一句话--

我们只需要知道,某个月的1号是星期几,就能把整个日历渲染出来

关于年月日的计算,我这边有两种模式,一种是只计算当月日期,另一种则是将整年的日期都计算出来。在本篇文章里我想着重记录第一种写法,大家想了解第二种的话可以到我的github里看看这个日历的demo。

我们先来个看图说话,这个二月份有28天,1号是星期四。那是不是说,我们只要从周四开始,按顺序渲染出28个'main__block'就好了呢?其实就是这样,关键是怎么把我们的1号定位到周四,只要这个能够准确定位到,我们的日历自然就出来了。

// 定义每个月的天数,如果是闰年第二月改为29天// year=2018;month=1(js--month=0~11)let daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {    daysInMonth[1] = 29;}// 获得指定年月的1号是星期几let targetDay = new Date(year, month, 1).getDay();// 将要在calendar__main中渲染的列表let total_calendar_list = [];let preNum = targetDay;// 首先先说一下,我们的日期是(日--六)这个顺序也就是(0--6)// 有了上述的前提我们可以认为targetDay为多少,我们就只需要在total_calendar_list的数组中push几个content为''的obj作为占位if (targetDay > 0) {    for (let i = 0; i < preNum; i++) {        let obj = {            type: "pre",            content: ""        };        total_calendar_list.push(obj);    }}复制代码

这样一来,1号的位置自然而然就到了我们需要的星期四了,接下来就只需要按顺序渲染就ok啦。下面是剩下日期数组填充,填充完毕之后return出来供我们view层使用。

for (let i = 0; i < daysInMonth[month]; i++) {    let obj = {        type: "normal",        content: i + 1    };    total_calendar_list.push(obj);}nextNum = 6 - new Date(year, month+1, 0).getDay()// 与上面的type=pre同理for (let i = 0; i < nextNum; i++) {    let obj = {        type: "next",        content: ""    };    total_calendar_list.push(obj);}return total_calendar_list;复制代码

怎么开发日历相关的功能?

如何选择上一个月或下一个月?

data() {    return {        // ...        selectedYear: new Date().getFullYear(),        selectedMonth: new Date().getMonth(),        selectedDate: new Date().getDate()    };}handlePreMonth() {    if (this.selectedMonth === 0) {        this.selectedYear = this.selectedYear - 1        this.selectedMonth = 11        this.selectedDate = 1    } else {        this.selectedMonth = this.selectedMonth - 1        this.selectedDate = 1    }}handleNextMonth() {    if (this.selectedMonth === 11) {        this.selectedYear = this.selectedYear + 1        this.selectedMonth = 0        this.selectedDate = 1    } else {        this.selectedMonth = this.selectedMonth + 1        this.selectedDate = 1    }}复制代码

就是这么简单,需要注意的点是跨年的时间转换,我们需要在变更月份的同时把年份也改变,这样才能渲染出正确的日期。

也许大家会有疑问,怎么变更了月份或年份之后不需要重新计算一次日期呢?其实是有计算的,不知大家是否还记得,vue可是数据驱动变更的,我们只需要关注数据的变更即可,其他东西vue都会帮我们解决。

如果选中某一天?

handleDayClick(item) {    if (item.type === 'normal') {        // do anything...        this.selectedDate = Number(item.content)    }}复制代码

在渲染列表的时候我就给每一个block绑定了click事件,这样做的好处就是调用十分方便,点击每一个block的时候,可以获取该block的内容然后do anything you like

当然我们也可以给外层的父级元素绑定事件监听,通过事件流来解决每个block的点击事件,这里看个人习惯~毕竟元素数量不是特别多

总结

一个移动端日历貌似也有惊无险的完成啦,总体来说日历这活还是偏样式方面的,对逻辑的要求不是特别高,对样式的要求倒是挺高的需要对flexbox布局有一定理解,才能迅速的吧日历的骨架搭起来,虽然也不一定说必须用flex,不过个人认为用flex的效率会稍高一些。

啰嗦一下,为什么想起来写日历?当然是业务需求啦,所以说这个日历组件一开始是react写的,后面想在vue里也尝试一下就改成了vue。其实在react里面写也是大同小异啦,只不过我会把日期的block抽离成无状态组件,也不为啥就感觉比较好看:)

转载地址:http://uyqyo.baihongyu.com/

你可能感兴趣的文章
java 栈帧与类的关系_深入理解Java虚拟机之类运行时栈帧结构
查看>>
php中删除评论怎么做的,详解PHP如何实现评论回复删除功能
查看>>
macports 安装php,「macports」MacOS 中 MacPorts 安装和使用 - 金橙教程网
查看>>
php 审计 for linux,for linux是什么意思
查看>>
matlab里面连接器是什么,Oops - an error has occurred
查看>>
matlab建立桌面图标,在ubuntu16.04上创建matlab的快捷方式(实现方法)
查看>>
smarty使用php代码,笑谈配置,使用Smarty技术_php
查看>>
oracle数据实际值限制,c# – Oracle数据库TNS密钥“数据源”的值长度超过了’128’的限制...
查看>>
silk v3 decoder php,解码转换QQ微信的SILK v3编码音频为MP3或其他格式
查看>>
linux不能访问80端口,lunux开放80端口(本地访问不了linux文件可能是这个原因)...
查看>>
android单位转换小程序,微信小程序中rpx与rem单位转换
查看>>
ps切图教程 android,PS前端切图完整教程
查看>>
html显示服务器状态,显示服务器时间并一直显示(html代码)
查看>>
在线html代码优化,网站seo优化html代码方法
查看>>
HTML如何把输入框变成必填值,required输入框为必填项
查看>>
html定位有几种,POSITION定位有哪几种?各有什么特点?
查看>>
背锅侠逆袭之路
查看>>
演示:使用协议分析器取证IPv6的报文结构
查看>>
oracle 11gr2 rac中的4种IP解说
查看>>
为什么你找不到工作?
查看>>