有这样一个普通的日历需求
第一反应就是找插件,结果找到了,但是改起来非常麻烦,然后查下实现的原理,发现原来很简单,于是自己实现了一个。
首先分析一下这个组件,每页显示的是
当前月的所有日期及所占据的行剩下的格子所属的上一个月的最后几天或下一个月的前几天。
然后,神奇的Date类型原来可以这样获取日期实例:
new Date(2018,4,0) // 2018年5月第一天的Date实例
new Date(2018,4,0) // 2018年4月最后一天的Date实例
new Date(2018,4,-1) // 2018年4月倒数第二天的Date实例
new Date(2018,4,32) // 2018年6月第一天的Date实例
所以,可以这样获取每一页的第一格的日期:
var monthFirstDate = new Date(2018, 4-1, 1) // 假设现在是四月
var monthFirstDay = monthFirstDate.getDay() // 本月第一天是星期几,星期日是0,星期一是1...
// 所以如果每行第一个是星期日,则每一页的第一格的日期:
var pageFirstDate = new Date(2018, 4-1, 1-monthFirstDay)
而本页的下个月的日期的规律是
下个月第一个星期天之前的日期
所以,可以这样获取一页日历所有日期的Date实例
/**
* @function getCalendarData
* @param {type} opts {
* day: 所在月的某一天的Date实例
* }
* @return {type} {当页所有日期的Date实例(数组)}
*/
function getCalendarData (opts) {
var opt = opts || {}
var _day = opt.day || new Date(), // Date实例,不传取今天
nowMonth = _day.getMonth(),
nowYear = _day.getFullYear(),
nowDate = _day.getDate()
var firstDate = new Date(nowYear, nowMonth, 1), // 本月第一天
firstDay = firstDate.getDay(), // 本月第一天是星期几
activeNum = 1
var result = []
// 例: new Date(2018, 4, 0)结果是2018年3月31日,new Date(2018, 4, -1)结果是2018年3月30日
while (!(isNextMonth(_day, new Date(nowYear, nowMonth, activeNum - firstDay)) &&
new Date(nowYear, nowMonth, activeNum - firstDay).getDay() === 0)) {
// 非(当天是下个月的日期&&当天是周日),则推入数组
// 从new Date(nowYear, nowMonth, 1 - firstDay)开始是为了填上日历当前页里的上一个月的日期
result.push(new Date(nowYear, nowMonth, activeNum - firstDay))
activeNum++
}
function isNextMonth (a, b) { // a,b为Date实例
return (b.getFullYear() === a.getFullYear() && b.getMonth() === a.getMonth() + 1) || // 两个月在同一年
(b.getFullYear() === a.getFullYear() + 1 && b.getMonth() === 0 && a.getMonth() === 11) // 不在同一年
}
return result
}
再之后,不管你用拼接html字符串还是用vue/react...,把上面的得出的Date实例数组循环一下,生成DOM,加上css,渲染出你想要的日历!