共计 2775 个字符,预计需要花费 7 分钟才能阅读完成。
改造项目
CodePen 原项目
场景
自己写个聚合首页,方便自己统一管理常用的网页工具,参考的是 Mac OS 的仪表盘,如下:
虽然短时间没法做到这么精美,但是起码功能得差不多吧,时钟与天气都已经 OK,在做日历的时候觉得比较麻烦,就去 CodePen 找找看,便有了如此。
源码迁移
CodePen 上的原项目本身难度不高,繁琐在理清楚日历逻辑(原作者逻辑),以及将此作者项目中关于 DOM 的操作转换成 vue 的模板操作。
先看逻辑
- 本月 1 号之前有可能存在上个月的尾巴(假如我们需要将日历方格全部填满)
- 本月内容以及当天的样式
- 本月最后一天后,也有可能会有下个月的月头
分别对应了原项目中这三个函数,原作者也有注释:
lastDayOfLastMonth 上个月最后一天,这里应该是原作者命名问题,应该是 lastDayOfPreviousMonth
firstDayOfMonth 本月第一天
lastDateOfMonth 本月最后一天
i 是本月第几天,走了一个 do while 循环
// If not Sunday but first day of the month
// it will write the last days from the previous month
if (i == 1) {
html += '<tr>';
var k = lastDayOfLastMonth - firstDayOfMonth+1;
for(var j=0; j < firstDayOfMonth; j++) {
html += '<td class="not-current">' + k + '</td>';
k++;
}
}
上面的意思就是第一天时候把上个月尾巴给加到表格标签里面(最后统一加)
// Write the current day in the loop
var chk = new Date();
var chkY = chk.getFullYear();
var chkM = chk.getMonth();
if (chkY == this.currYear && chkM == this.currMonth && i == this.currDay) {html += '<td class="today">' + i + '</td>';} else {html += '<td class="normal">' + i + '</td>';}
上面特殊处理的今天,其他本月内容都是常规加入,可以从 class 上面区分看出来。
// If not Saturday, but last day of the selected month
// it will write the next few days from the next month
if (i == lastDateOfMonth) {
var k=1;
for(dow; dow < 6; dow++) {
html += '<td class="not-current">' + k + '</td>';
k++;
}
}
上面的意思就是当本月最后一天的时候,顺便把下个月的月头加进来。
漏了,每行的首尾控制
// If Sunday, start new row
if (dow == 0) {html += '<tr>';}
// If Saturday, closes the row
if (dow == 6) {html += '</tr>';}
这里说一下,因为日历是从周日到周六为一行,所以作者这里判断方案是 Sunday 与 Saturday。
上面这些就是核心代码,当然还有下个月与上个月切换,不过就是清空当前日历再来一次,这些看源码即可,没有太多逻辑问题。
转 vue
转起来难度不是很大,更多的是思维变化。
原项目是直接操作 DOM,我们这里通过数据操作模板(姑且区分下),所以需要构建一个对象来承接每个”天“。
我们申明一个日历数组,由于日历是由若干个星期组成,所以我们就命名为:weeks,然后每个”天“给对象,属性如下:
{
// 哪天
label: [string],
// 是否今天
today: [boolean],
// 是否本月
current: [boolean],
}
那么我们的”天“会有这几种情况:
- 常规天
- 今天
- 上月或下月的天
{
// 哪天
label: [string],
// 是否本月
current: true,
}
{
// 哪天
label: [string],
// 是否今天
today: true,
// 是否本月
current: true,
}
{
// 哪天
label: [string],
}
接下来,我们把原逻辑里面对应的 html,换成 weeks,tr 换成 week(每次新建一个空数组),在周日或者第一天时候把 week 置空,在周六或本月最后一天把 week 闭合并且让 weeks 来 push 一下 week,循环走完,我们一个月的关于 weeks、week 以及”天“的处理就完成了。
核心逻辑如下:
const y = this.year, m = this.month
let d = new Date()
// 本月第一天
, firstDayOfMonth = new Date(y, m, 1).getDay()
// 本月最后一天
, lastDateOfMonth = new Date(y, m + 1, 0).getDate()
// 上个月最后一天
, lastDayOfPreMonth = m === 0 ? new Date(y - 1, 11, 0).getDate() : new Date(y, m, 0).getDate()
let week = []
// 月第一 = 几天
let i = 1
do {let dow = new Date(y, m, i).getDay()
// 周日则单独一行
if(dow === 0) {week = []
}
// 每月第一天关于上个月尾数几天当月显示处理
else if(i === 1) {
let k = lastDayOfPreMonth - firstDayOfMonth + 1
for(let j = 0; j < firstDayOfMonth; j++) {
let obj = {label: k,}
week.push(obj)
k++
}
}
// 查询当天,高亮处理
let chk = new Date()
let chkY = chk.getFullYear()
let chkM = chk.getMonth()
if(chkY === this.year && chkM === this.month && i === this.day) {
week.push({
label: i,
// 本月
current: true,
today: true,
})
// 其他本月常规天
} else {
week.push({
label: i,
// 本月
current: true,
})
}
// 周六则闭合
if(dow === 6) {this.weeks.push(week)
}
// 本月最后一天,处理下个月首几日在当月内显示
else if(i === lastDateOfMonth) {
let k = 1
for(dow; dow < 6; dow++) {
let obj = {label: k,}
week.push(obj)
k++
}
this.weeks.push(week)
}
i++
} while(i <= lastDateOfMonth)
注意跨年问题
最终效果