自定义日历实现

290 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

一、前言

最近项目需要实现一个交易日历,没有很好的组件直接使用,于是就自己实现了下,大概样子如下图所示 image.png

二、核心实现

我们可以分为三部分来看:

  1. 头部:可以选择年份、月份、上下月份切换(和下面核心)
  2. 周:比较简单定义个数组
const CALENDAR_WEEKS = ['日', '一', '二', '三', '四', '五', '六']
  1. 核心日期怎么摆放,下面我们着重分析这个
  • 首先我们知道每个月多少天,唯一一个有变动的就是闰年二月,所以我们可以先定义每月天数的数组,二月加个判断
  const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

  // 对闰年二月天数处理
  if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
    daysInMonth[1] = 29
  }
  • 然后我们通过getDay()确定每月第一天,最后一条在周几,然后我们就可以知道上月、下月在当前日历需要显示几天;然后把上月、当月、下月日子合并一个数组去渲染。

核心代码

export const getAllDaysForYear = year => {
  /**
   * monthData 每月数据 用于最后输出
   * daysInMonth 每个月的天数
   * specialDaysInMonth 每个月第一天和最后一天的星期
   */
  const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

  // 对闰年二月天数特殊处理
  if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
    daysInMonth[1] = 29
  }
  const monthData = new Array(12).fill(null)
  // eslint-disable-next-line
  const specialDaysInMonth = monthData.slice(0).map((m, i) => {
    return [new Date(year, i, 1).getDay(), new Date(year, i, daysInMonth[i]).getDay()]
  })
  // eslint-disable-next-line
  return monthData.map((m, i) => {
    const month = []
    const pre = preDaysCreator(daysInMonth[i === 0 ? 11 : i - 1], specialDaysInMonth[i][0])
    const normal = normalDaysCreator(daysInMonth[i])
    const next = nextDaysCreator(specialDaysInMonth[i][1])
    return month.concat(pre, normal, next)
  })
}

const nextDaysCreator = lastDay => {
  const nextDays = []
  const count = 6 - lastDay
  for (let i = 0; i < count; i++) {
    const obj = {
      content: i + 1,
      type: 'next',
    }

    nextDays.push(obj)
  }
  return nextDays
}

const preDaysCreator = (preLastDay, firstDay) => {
  const preDays = []
  for (; firstDay > 0; firstDay--) {
    const obj = {
      content: preLastDay--,
      type: 'pre',
    }

    preDays.splice(0, 0, obj)
  }
  return preDays
}

const normalDaysCreator = days => {
  const normalDays = []
  for (let i = 0; i < days; i++) {
    const obj = {
      content: i + 1,
      type: 'cur',
    }

    normalDays.push(obj)
  }
  return normalDays
}

注意:getDay() 方法可返回一周(0~6)的某一天的数字;星期天为 0, 星期一为 1, 以此类推。

如果你页面不是从周日开始摆放,注意处理上月、下月方法循环的判断就好。