日历插件并不少见,但多数都是大而全,直接使用插件的话往往会使项目变得臃肿,而且想做一些定制化的修改,成本反而会更高。
最近为了练手Taro
框架,做了一个日历微信端小程序,大海日历,刚好用到日历输出。项目开始时也想偷个懒直接找网上插件来使用,但找来找去,大多数插件都做的大而全,觉得比较臃肿,而且定制成本都偏高,当然也不排除是我自己没找到的原因哈。于是,综合分析一下,还是自己动手吧。
需求分析
首先描述一下需求:
- 输出本月日期,按照星期分成行;
- 空余位置用其他月份日期补全;
- 某些月份日期占
6
行,某些月份日期占5
行,为了切换月份时保证视觉效果,我们要固定所有月份都按6
行铺排;
- 西方习惯周日为每周第一天,中国更习惯周一为每周第一天,所以要支持此设置。
我们从以上需求可分析出:
- 首先需要计算本月第一天的星期;
- 当前可视日期数量为
6 * 7 = 42
;
- 支持传入”每周第一天是否是周一”参数;
- 输出为
7 * 6
结构二位数组。
网上很多实现,是通过月份天数、本月第一天的星期,来计算本月实际占几行,进而来判断是否需要补充一行其他月份日期。我们这里取个巧,不进行此判断,不管需不需要,我们都地毯式的循环输出42
天,减少很多计算量。
另外还有一个可取巧的点,Date
类支持设置日期为任意整数,并且程序会自动纠正成对应日期,比如:
1 2 3 4
| new Date(2022, 10, 1) new Date(2022, 10, 0) new Date(2022, 10, -1) new Date(2022, 10, 31)
|
注意:
设置月份时,和通过getMonth()
获取月份时、通过getDay()
获取星期时,使用的都是编程习惯的索引值,而非自然值,也就是计数从0
开始;
月份范围是[0-11]
;星期范围是[0-6]
,0
代表周日,也就是默认星期是以周日开始的;
而通过getDate()
获取日期时,则使用的是自然日。
思路
![日历第一天传入的date值]()
日历第一天传入的date值
通过上图,得出,当本月第一天在周一到周日变化时,日历第一行第一天的日期取值范围为[1, 0, -1, -2, -3, -4, -5]
,我们利用本月月第一天的星期索引,即可算出具体日期:
- 取得本月第一天的星期索引,取值范围为
[0, 1, 2, 3, 4, 5, 6]
;
- 当设置每周第一天为周日时,可通过
1 - new Date().getDay()
计算得出第一行第一天的具体日期;
- 当设置每周第一天为周一时,本月第一天为周一到周六日期可通过
2 - new Date().getDay()
计算得出,为周日时则设定为-5
;
- 循环输出所有日期,我们可以设置两层循环,第一层循环用来设定当前日历的1-6周,第二层则用来设定每周的具体日期。
方法源码
具体方法实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
Date.prototype.Format = function (fmt = "yyyy-MM-dd") { var o = { "M+": this.getMonth() + 1, "d+": this.getDate(), "h+": this.getHours(), "m+": this.getMinutes(), "s+": this.getSeconds(), "q+": Math.floor((this.getMonth() + 3) / 3), "S": this.getMilliseconds() };
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) { if (new RegExp("(" + k + ")").test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); } }
return fmt; } const genCalendarDays = (currentDate = new Date(), isSundayFirst = false) => { const days = []; const currentYear = currentDate.getFullYear() const currentMonthIndex = currentDate.getMonth()
let firstDayWeekIndex = (isSundayFirst ? 1 : 2) - new Date(currentDate.getFullYear(), currentDate.getMonth()).getDay() firstDayWeekIndex = firstDayWeekIndex < -5 ? 1 : firstDayWeekIndex > 1 ? -5 : firstDayWeekIndex for (let rowIndex = 0; rowIndex < 6; rowIndex++) { days.push([]) for (let weekIndex = 0; weekIndex < 7; weekIndex++) { const date = new Date(currentYear, currentMonthIndex, rowIndex * 7 + weekIndex) days[rowIndex].push({ date, formatString: date.format() }) } } return days }
|
日历效果,可以微信搜索大海日历小程序来体验。
其实日历核心部分也就短短十几行代码,相比臃肿的插件,瞬间感觉清爽不少,在做一些定制化需求时,也灵活多了。
本文永久链接: https://www.mulianju.com/javascript-calendar/