小程序滚动日历组件

小程序滚动日历组件

前段时间产品提出这个需求,要求做一个日历容器,并要求是滚动,不能是那种每个月切换的容器,所以我就在网上大概查找了一下类似组件,滚动的容器较少,于是我就自己写了这个组件,以下是大概的js思路,wxml和css就不附上了,每个UI都有不同的想法

  1. 在ready中初始化组件数据

    /**
     * @description: 初始化
     */
    ready() {
      // 这两个数据是从页面中传过来的,起始时间和结束时间
      // 数组类型 [year, month, day]
      const { endDate, startDate } = this.data
      let monthList = [] // 这个用于wxml中遍历,以每月为item
    
      // 未跨年
      if ( endDate[0] === startDate[0]) {
        let rangeMonth = endDate[1] - startDate[1]
        for (let i = 0; i <= rangeMonth; i++) {
          let date = [startDate[0], startDate[1] + i + 1]
          // monthInitData()初始化每月数据
          let days = this.monthInitData(startDate[0], startDate[1] + i)
          monthList.push({
            date,
            days,
          })
        }
      } else {
        // 跨年前月份
        for (let i = 0, lng = 12 - startDate[1]; i < lng; i++) {
          let date = [startDate[0], startDate[1] + i + 1]
          let days = this.monthInitData(startDate[0], startDate[1] + i)
          monthList.push({
            date,
            days,
          })
        }
    
        // 跨年后月份
        for (let i = 0; i <= endDate[1]; i++) {
          let date = [endDate[0], i + 1]
          let days = this.monthInitData(endDate[0], i)
          monthList.push({
            date,
            days,
          })
        }
      }
      this.setData({
        monthList,
      })
    }
  2. 初始化每月数据:

    逻辑是获取每个月第一天的星期数和每个月的最大天数,然后在之前和之后的空数据填充item占位
    然后遍历,根据不同的需求,在遍历中处理,最后在html中flex布局即可

    /**
     * @description: 初始化每月数据
     * @param: { number } year 年
     * @param: { number } month 月
     */
    monthInitData(year, month) {
      let { calendarValue, startDate, endDate }= this.data, // 当前点击时间,起始时间,结束时间
        firstWeek = new Date(year, month, 1).getDay(), // 本月的第一天的星期数
        days = [], // 日期数组
        max = this.getMonthMax(year, month + 1), // 每月最大天数
        lastWeek = new Date(year, (month + 1), 0).getDay() // 本月的最后一天的星期数
    
      // 填充每月一号之前的空数据
      if (firstWeek > 0) {
        for (let i = 0; i < firstWeek; i++) {
          days.push({
            day: 0
          })
        }
      }
    
      let currentTime = 0, 
          startTime = 0,
          endTime = 0
    
      if (calendarValue[0]) {
        currentTime = new Date(calendarValue[0], calendarValue[1], calendarValue[2]).getTime()
      }
      if (startDate[0]) {
        startTime = new Date(startDate[0], startDate[1], startDate[2]).getTime()
      }
      if (endDate[0]) {
        endTime = new Date(endDate[0], endDate[1], endDate[2]).getTime()
      }
    
      for (let i = 1; i <= max; i++) {
        // 以下状态是需要在html中判断处理,根据自己需求改变
        let current = false, // 当前点击状态
          disabled = false, // 禁止状态
          now = false, // 今天显示文案
          time = new Date(year, month, i).getTime(),
          nowTime = [new Date().getFullYear(), new Date().getMonth(), new Date().getDate()]
        if (currentTime && currentTime == time) current = true
        if (startTime && startTime > time) disabled = true
        if (endTime && endTime < time) disabled = true
        if (year === nowTime[0] && month === nowTime[1] && i === nowTime[2]) now = true
    
        days.push({
          day: i,
          current,
          disabled,
          now,
        })
      }
    
      // 填充每月最后一天之后的空数据
      if (lastWeek != 6) {
        for (let i = 0, l = (6 - lastWeek); i < l; i++) {
          days.push({
            day: 0
          })
        }
      }
      return days
    },
    
    /**
     * 获取一个月最大天数
     * @method getMonthMax
     * @param: { number } year 年份
     * @param: { number } month 月份
     */
    getMonthMax(year, month) {
      let max = 31
      if (month == 4 || month == 6 || month == 9 || month == 11) {
        max = 30
      } else if (month == 2) {
        if (((year % 4) == 0 && (year % 100) > 0) || (year % 400) == 0) {
          max = 29
        } else {
          max = 28
        } 
      }
    
      return max
    },
    
  • 以上就是核心逻辑,像一些弹窗事件和一些点击事件就不附上来了