手动开发一个日历组件

5,281 阅读3分钟

前言

应用场景是公司的一个每日推荐专题需要加入日历功能,点击日历可以跳转到当日的推荐。

设计图:

关于日历的实现

日历实现的核心是利用js的Date对象(项目代码环境是依赖在vue当中)。

第一步:首页展示

我们首先考虑我们日历的第一页,当前月。

要展示我们的当前月,首先我们需要三个数据:

  • 当前年份
  • 当前月份
  • 月份下的日期列表

这些数据的初始化(注意这里只是初始化,因为在日历的使用过程中,他们是会随着交互而变化的)状态很好获得,前两个数据我们只需要通过:

this.curdate = new Date();
this.calendarMonth = this.curDate.getMonth() + 1; // 注意月份加1
this.calendarYear = this.curDate.getFullYear();

值得一提的是我们的日期数据,因为这里涉及到2个变量,第一个是每个月份的天数是不一样的。第二个是每个月的1号是星期几也是不固定的(而这个星期几,则会影响到显示上,1号应该从哪里开始)。

直接贴上最终的获取列表的代码:

/* 返回当月的日期表 */
getMonthList() {
  // new Date()中的最后一个参数为0,表示的是当月的最后一天,
  // 因此我们通过这一句获得的就是传入的年月的当月最后一天。
  let date = new Date(this.calendarYear, this.calendarMonth, 0);
  // 获得当月的最后一天的日期号,就知道了当月有多少天
  this.days = date.getDate();
  // setDate(1)的作用是把日期变为当月的第一天
  date.setDate(1);
  // 在通过getDay(),就能知道当月的第一天是星期几
  let day = date.getDay();
  // 获得1号是星期几后,在日历上做一个偏移,这里我采用的是补0
  let delay = day - 1;
  // 因为星期天在getDay的返回中是0,固特殊处理
  if (day == 0) {
  	delay = 6;
  }
  let list = [];
  for (let i = 0; i < this.days + delay; i++) {
    if (i < delay) {
      list.push(0);
    } else {
      list.push(i - delay + 1)
    }
  }
  return list;
},
// 通过这个函数我们就获得了当月的列表,格式类似于[0,0,0,1,2,3...31]

第二步:响应变化

变化主要分为两个方面:点击左右箭头改变月份,点击具体的日期。

改变月份:

改变月份时我们要做的主要工作是改变我们用于记录时间的变量(使用mvvm框架的好处是我们不需要关注视图层面的改变,只需要专注我们的变量):

changeMonth(flag) {
  // flag 记录左右滑动,对应到月份就是加1和减1
  this.curDate.setMonth(this.calendarMonth - 1 + flag);
  // setNowDate() 函数里面包括了我们每次改变日期时所需要做的工作,比如刷新月份列表(即重新调用getMonthList函数等其他一些功能)
  //如果我们要把日历做成一个组件,那么我们的函数中肯定需要触发一个事件钩子以反馈给外部例如:$emit('monthChange')
  this.setNowDate();
},
改变日期:
changeDay(day, el) {
  // 改变我的当前显示日期
  this.curDate.setDate(day);
  // clickedMonth是用来记录我当前点击的月份,这个变量只随点击改变,而不随页面翻动改变,主要是用来判断当前日期的样式用的。
  this.clickedMonth = this.calendarMonth;
  this.clickedYear = this.calendarYear;
  // 获取当前日历的推荐数据(具体逻辑不赘述,这里可以进行自己需要的操作)
  this.getData(new Date(this.calendarYear, this.calendarMonth - 1, day), true)
  this.setNowDate();
},

点击了日期后,我们的样式也会随之改变:

// 计算样式的函数
calDayCls(day) {
  let cls = "calendar-li";
  /* 查看当前 */
  if (day == this.calendarDay && this.calendarYear == this.clickedYear && this.calendarMonth == this.clickedMonth) {
    cls += ' calendar-li-active';
  }
  return cls
}

至此,我们的日历的核心功能就完成了,核心函数如下:

setNowDate // 改变控制当前日历显示的变量
changeMonth // 改变月份
getMonthList // 获取月份日期表
changeDay // 改变日期
calDayCls // 计算当前日期的样式

有了这些核心部分,就基本实现了日历的核心功能,然后就可以在上面添加更多的自定义的功能了,因为很多是涉及到我自己项目中个性化的东西,不具备过多参考价值,固没有在代码体现。