面向初学者:如何写一个日历组件

您所在的位置:网站首页 日历开工吉日怎么写 面向初学者:如何写一个日历组件

面向初学者:如何写一个日历组件

2024-07-08 13:00| 来源: 网络整理| 查看: 265

最近刚好给自己的组员讲解日历组件的开发,就顺便写下自己的心得。

这是苹果上的日历截图,从上面的内容来看,有公历,农历,24节气和节日。

遗憾的是目前还未找到 公历 和 农历 转换的公式;现阶段能够找到的开源的日历组件,基本上都是存了一份 农历 1900-2100 的润大小信息表,有兴趣的可以阅读下 农历编算法则 。

而节日相对而言会受到地区等因素影响,需要定时维护,所以实际上,我们在日历上真正能够计算并使用的只有 公历 和 24节气。

写日历需要解决的问题 1. 每周从什么时候开始?

这是因为不同的地区,每周开始的时间是不一样的,可以参考知乎的 一周的第一天是周一还是周日?

平时在使用实体日历的时候,的确是有些印刷的日历会从 星期一 开始,而使用电子产品的时候,大多数都是从 星期天 开始的。

不过这个问题还是比较好解决的,首先看下日历的头部。

从截图上看出,不管是那天开始,整体的顺序不变,而且有没有发现,这个情况和无缝滚动的组件很像,内容滚出视图外,自动补充到末尾,就像一个圆环。

那我们基于无缝滚动的原理,就很容易获得一周7天的数字。

既然原理分析出了,那我们就可以很容易的写出代码:

// 在 js 中,0 是周日,0 ~ 6 代表周一至周日 const WEEKDAYS = [0, 1, 2, 3, 4, 5, 6] // 一周只有7天,不会有第8天,所以只要拷贝一次就可以了 const DOUBLE_WEEKDAYS = WEEKDAYS.concat(WEEKDAYS) /** * 获取获得一周7天的数字 * * @param firstWeekDay 周开始时间 * * @return 周数组 */ function getWeekdays(firstWeekDay = 0) { if (firstWeekDay === 0) return WEEKDAYS return DOUBLE_WEEKDAYS.slice(firstWeekDay, firstWeekDay + 7) }

我们执行之后,基本上达到我们的预期。

那我们再进一步,转化成日历的头部,顺便支持国际化

// 简体中文 const zh = { // 完整名称 weekdays: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], // 短名称 weekdaysShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], // 缩写 weekdaysAbbr: ['日', '一', '二', '三', '四', '五', '六'] } // 英文 const en = { // 完整名称 weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // 短名称 weekdaysShort: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // 缩写 weekdaysAbbr: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] } /** * 获取一星期的名称列表 * * @param {Number[]} weekdays 一周7天的数字 * * @param {Object[]} */ function getWeekHead(weekdays, locale) { return weekdays.map(day => ({ name: locale.weekdays[day], short: locale.weekdaysShort[day], abbr: locale.weekdaysAbbr[day], day: day })) }

打开 Chrome devtools 执行下,基本额可以达到我们的预期

2. 每个月的第一天不是周的开始

这个很正常,周的开始都不确定,何况是月天数不固定。

我们从上面的图也可以看出来,月结束和开始都不固定,那我们该如何处理?

其实这个也非常容易解决,我们先不从程序的角度思考,单纯从图片上思考下,是不是只要知道每个月的1号在处在星期中的那一天,在往前减去多少天,就可以计算出 1号所在星期 开始的时间。

可能又点绕,我们用图片说话:

那我们知道如何计算之后,就可以开始准备数据了。

首先我们需要知道,1号是星期几,这个在 js 中,直接通过 Date#getDay() 就可以知道是星期几

但因为前面说过了,周的开始是不固定的,所以我们计算的时候,还需要前面的函数辅助才能知道应该减少几天。

到这里可能会有人疑惑,每个月份的天数都不固定,而且还有润月的问题;就算我们知道要减少几天,计算上一个月的最后一天也是一个麻烦事情,这个应该如何解决呢?

其实这个也非常简单,在 js 中 Date 对象已经帮帮我们处理好了。

到这里我们所有的条件都已经准备完毕了,剩下的就交给循环了。

/** * 获取月份日历 * * @param {*} year 年 * @param {*} month 月 * @param {Object} options * @param {Number[]} options.weekdays 一周7天的数组 * @param {Number} [options.firstWeekDay=0] 周开始时间 * @param {Number} [options.visibleWeeksCount=6] 单个日历上显示的周数量 */ function getMonthCalendar(year, month, options) { const weekdays = options.weekdays const cursor = new Date(year, month - 1, 1, 0, 0, 0, 0) const count = (options.visibleWeeksCount || 6) * 7 // 让时间定位在周开始的时间 cursor.setDate(cursor.getDate() - weekdays.indexOf(cursor.getDay())) const calendar = [] let week = [] for (let i = 0; i < count; i++) { if (!(i % 7)) { week = calendar[i / 7] = [] } week.push( // 拷贝时间 new Date(cursor) ) cursor.setDate(cursor.getDate() + 1) } return calendar } function format(d) { return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, 0)}-${d.getDate().toString().padStart(2, 0)}` }

我们看下执行结果

试试周一开始

目前看来,日期和星期都按我们预想的那样执行了,剩下的就是渲染问题。

渲染日历

其实当我们数据准备好的那一刻,基本上这个日历也就差不多完成了,剩下的是往上面堆功能。

我们只要数据处理好,那我们就可以使用任何框架,甚至在任何支持 Date 对象的 js 环境中运行了,比如在 nodejs 上运行。

所有代码都在这里:完整代码

用 nodejs >= 12.x 试试: /** * 打印日历 */ function printCalendar(year, month, firstWeekday) { const weekdays = getWeekdays(firstWeekday) const calendar = getMonthCalendar(year, month, { weekdays }) const columns = getWeekHead(weekdays, zh).map((w) => w.abbr) const rows = calendar.map((dates) => { const row = {} columns.forEach((weekday, index) => { const date = dates[index] if (isCurrentMonth(date)) { row[weekday] = date.getDate() } }) return row }) function isCurrentMonth(d) { return d.getFullYear() === year && d.getMonth() + 1 === month } const time = ` ${year}-${month.toString().padStart(2, '0')}` console.log('') console.group(time) console.table(rows, columns) console.groupEnd(time) }

打印 2021-01 月,以周日开始的日历

printCalendar(2021, 1, 0)

打印 2021-01 月,以周一开始的日历

printCalendar(2021, 1, 1)

渲染到 html 上

结束

农历和节假日可以通过数据的方式手动维护,不过节气可以维护到组件中。

写一个日历,也是一件比较麻烦的事情,幸好大部分前端用到的日历都不要求 农历,24节气 和 节日,不然头发都要多掉好几根。

如果自己维护日历数据的模块,可以试试我发布的包:@zhengxs/calendar-data,欢迎 fork

掘金年度征文 | 2020 与我的技术之路 征文活动正在进行中......



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3