快速输出日历

日历插件并不少见,但多数都是大而全,直接使用插件的话往往会使项目变得臃肿,而且想做一些定制化的修改,成本反而会更高。

最近为了练手Taro框架,做了一个日历微信端小程序,大海日历,刚好用到日历输出。项目开始时也想偷个懒直接找网上插件来使用,但找来找去,大多数插件都做的大而全,觉得比较臃肿,而且定制成本都偏高,当然也不排除是我自己没找到的原因哈。于是,综合分析一下,还是自己动手吧。

需求分析

首先描述一下需求:

  1. 输出本月日期,按照星期分成行;
  2. 空余位置用其他月份日期补全;
  3. 某些月份日期占6行,某些月份日期占5行,为了切换月份时保证视觉效果,我们要固定所有月份都按6行铺排;
  4. 西方习惯周日为每周第一天,中国更习惯周一为每周第一天,所以要支持此设置。

我们从以上需求可分析出:

  1. 首先需要计算本月第一天的星期;
  2. 当前可视日期数量为6 * 7 = 42
  3. 支持传入"每周第一天是否是周一"参数;
  4. 输出为7 * 6结构二位数组。

网上很多实现,是通过月份天数、本月第一天的星期,来计算本月实际占几行,进而来判断是否需要补充一行其他月份日期。我们这里取个巧,不进行此判断,不管需不需要,我们都地毯式的循环输出42天,减少很多计算量。

另外还有一个可取巧的点,Date类支持设置日期为任意整数,并且程序会自动纠正成对应日期,比如:

new Date(2022, 10, 1) // 得出日期2022-11-1
new Date(2022, 10, 0) // 得出日期2022-10-31
new Date(2022, 10, -1) // 得出日期2022-10-30
new Date(2022, 10, 31) // 得出日期2022-12-1

注意:
设置月份时,和通过getMonth()获取月份时、通过getDay()获取星期时,使用的都是编程习惯的索引值,而非自然值,也就是计数从0开始;
月份范围是[0-11];星期范围是[0-6]0代表周日,也就是默认星期是以周日开始的;
而通过getDate()获取日期时,则使用的是自然日。

思路

日历第一天传入的date值

日历第一天传入的date值

通过上图,得出,当本月第一天在周一到周日变化时,日历第一行第一天的日期取值范围为[1, 0, -1, -2, -3, -4, -5],我们利用本月月第一天的星期索引,即可算出具体日期:

  1. 取得本月第一天的星期索引,取值范围为[0, 1, 2, 3, 4, 5, 6]
  2. 当设置每周第一天为周日时,可通过1 - new Date().getDay()计算得出第一行第一天的具体日期;
  3. 当设置每周第一天为周一时,本月第一天为周一到周六日期可通过2 - new Date().getDay()计算得出,为周日时则设定为-5
  4. 循环输出所有日期,我们可以设置两层循环,第一层循环用来设定当前日历的1-6周,第二层则用来设定每周的具体日期。

方法源码

具体方法实现如下:

/**
* 扩展Date原型,增加日期格式化方法
* 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
* @param fmt 时间格式
* @param second 秒
*/
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()

  // 计算当前可视区域,第一行第一天,应设置的date值,也就是传入到new Date()类的第三个参数
  // 取值范围:[-5, -4, -3, -2, -1, 0, 1]
  // 通过本月1日的星期数来计算date值,因为默认getDay()是从0(周日)开始的,所以需要补1;当周一作为每周开始时,需要补2
  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
}

日历效果,可以微信搜索大海日历小程序来体验。

其实日历核心部分也就短短十几行代码,相比臃肿的插件,瞬间感觉清爽不少,在做一些定制化需求时,也灵活多了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mulianju

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值