日历组件开发思想

294 阅读2分钟

写日历需要注意的两个问题

  1. 每周从周几开始,从周日开始还是从周一开始,像苹果上的日历就是从周日开始

image.png

image.png

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

p1-juejin.byteimg.com/tos-cn-i-k3…

基于这个原理,很容易得到一周七天的数字

// 在 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) 
}

执行后的结果

image.png

获取日历头部,顺便支持国际化

// 简体中文
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
  }))
}

执行结果

image.png

  1. 每个月的第一天不是从一号开始的

image.png

从图上可以看到,月开始和月结束都是不固定的,这该如何处理

只要知道每个月的一号是周几,在往前减去几天,就可以得出日期开始的时间

可能有点绕,看图

image.png

在js中 可以通过Date.getDay()方法知道是星期几

因为前面说过,周开始是不固定的(周日开始或周一开始),还要前面的函数才知道要减去几天

image.png

image.png

每个月的天数不一致,关于闰月的问题,js的Date对象已经帮我们处理好了

image.png

接下来就是循环产生数据了

/**
 * 获取月份日历
 *
 * @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)}`
}

执行结果

image.png

image.png

然后就是往页面上渲染数据了

此文章参考juejin.cn/post/691058…

代码理解 a指向b[0] b[1].....

// let a = []
// const b = []
// for(let i = 0; i < 42; i ++) {
//     if (!(i % 7)) {
//         a = b[i / 7] = [] // a = b[0] = []
//     }

//     a.push(i) === b[0].push(i)
// }

// console.log(b)