小程序日历

日历组件

自定义滑动日历组件
小程序折叠伸展与滑动日历

在这里插入图片描述

小程序滑动日历切换

效果图

在这里插入图片描述
在这里插入图片描述

代码

首先在components文件夹创建一个公共组件
取名为calendar

calendar组件代码

wxml

<!-- 日历组件 -->
<view class="calendar">
	<view class="title flex">
		<view class="flex">
			<!-- <picker value="{{selectDay.year+'-'+selectDay.month}}" bindchange="editMonth" mode="date" fields="month" class="year-month">{{selectDay.year}}.{{selectDay.month>9?selectDay.month:"0"+selectDay.month}}</picker>
			-->
			<!--<view class="icon" bindtap="lastMonth" style="transform:rotate(180deg);">
				<view class="iconfont icon-playfill" />
			</view>
			<view class="icon" bindtap="nextMonth">
				<view class="iconfont icon-playfill" />
			</view>-->

		</view>
		<view class="month-top">{{selectDay.year}}</view>
		<view class="month-top">{{selectDay.month}}</view>
		<view catchtap="openChange" class="flex open">
			<view>{{open?"收起":"展开"}}</view>
			<view style="margin-left:6rpx;font-size:20rpx" class="iconfont icon-{{open?'fold':'unfold'}}" />
		</view>
	</view>

	<!-- 日历头部 -->
	<view class="flex-around calendar-week">
		<view class="view calendar-week-clo"></view>
		<view class="view"></view>
		<view class="view"></view>
		<view class="view"></view>
		<view class="view"></view>
		<view class="view"></view>
		<view class="view calendar-week-clo"></view>
	</view>
	<!-- 日历主体 -->
	<!-- 这边是左滑右滑 -->
	<view
	 catchtouchstart='touchStart'
	 catchtouchend="touchEnd"
	 class="flex-start flex-wrap calendar-main"
	 style="height:{{dateList.length/7*72}}rpx"
	>
		<view wx:for="{{dateList}}" wx:key="dateList" class="day">
			<view
			 class="bg {{(item.year === selectDay.year && item.month === selectDay.month) ? (item.day === selectDay.day?'select':'') : 'other-month'}}"
			 catchtap="selectChange"
			 data-day="{{item.day}}"
			 data-year="{{item.year}}"
			 data-month="{{item.month}}"
			 data-date-string="{{item.dateString}}"
			>
			{{item.day}}
			</view>
			<view class="spot" wx:if="{{item.spot}}" />
		</view>
	</view>
</view>


wxss

/* components/calendar/calendar.wxss */
@font-face {
  font-family: 'iconfont';
  src: url('iconfont.eot?t=1596614903470');
  /* IE9 */
  src: url('iconfont.eot?t=1596614903470#iefix') format('embedded-opentype'),
    /* IE6-IE8 */
      url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAL8AAsAAAAABxQAAAKvAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDHAqBVIFEATYCJAMQCwoABCAFhG0HQhswBsgekiRFQgQU8AMAmmCIh3/HXu9L8hFVWwXoyALpqqoqwAEfD6g66abEPBux/13TPwBUeZ0lAldI75LS0ctlzLY8NwVk3NQm5Co3lqhaWPE+z+X0pi+Q1bez3MZatCYtPuoFGAcU4F7UiyIroBPkltpFHBi88q89JtA0p1jEYe/wNMQryKpAPHbYMMT3CooStetCdeZgEd9U1NPX9ADgQf8+/kFlxJNUMrLx5LpHhPaf+GUsavP/JpxJQpHgHq8gYx0oxO1s41yUiMQoTWOibQN1dYmf+Dn0MhYO2zgfttC/PEKSiSrS3QabXjPzE3ON4GdII/FrrEsGhPjr/dYGcOyzM56gg6PM1BXvcbZIxY7zzRyS4znJs/m2VmjuRNq9edF1kcx9FbW85/nRgbXX6s5q/AX+vsGh/kBhUUHAYszwFx4acaAA6l7k9bgjo8Vbue4acDcbCqFqcBru+htf4l/I4EnzzQAa8lT+kNoAyG/TJuTdyDcsG+NB4r05rfm73irgx/jRrw4XMB+gyga24dQGHCqlg8RKyZ3cWuWLEWhFQlMTmOwwFLrCPwH3E+omB/I1sy2yunmysOuoaNlEVd0Omtb0HW8ZYaJEaWDVnoPQd4ek6zuyvkeysE+omPpCVT+i0HQaWRe2LIYjsYsRzImIrEvIIagKZQmeaHoniW1Owqzr0NIsYbqjjCpKy4ftPqIQdsUWfd5WyTlFlKky6nWeI5KkIo2pTiLwUjvnWmNZGZ37UKmgypDoAENgHCFCrJYgDgKVgsrxXOLo+5MImzkSjK3oFuazCEbnmB6pUKocQPZ5FZDuV97RzbOpxHEUQjEqGdLrzCMkEhWizc9zIgRcKfuBmqZRGR1Fod7S5/3yH56AJnJDiRQ5SlRU3yhUlcRoMStek/ASdUgSAAA=')
      format('woff2'),
    url('iconfont.woff?t=1596614903470') format('woff'),
    url('iconfont.ttf?t=1596614903470') format('truetype'),
    /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
      url('iconfont.svg?t=1596614903470#iconfont') format('svg');
  /* iOS 4.1- */
}

.iconfont {
  font-family: 'iconfont' !important;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  line-height: 1;
  font-weight: normal;
}

.icon-unfold:before {
  content: '\e661';
}

.icon-fold:before {
  content: '\e6de';
}

.icon-playfill:before {
  content: '\e74f';
}

.flex {
  display: flex;
  justify-content: center;
  align-items: center;
}

.direction-column {
  flex-direction: column;
}

.flex1 {
  flex: 1;
}

.flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

.flex-start {
  display: flex;
  justify-content: flex-start;
  align-items: center;
}

.flex-end {
  display: flex;
  justify-content: flex-end;
  align-items: center;
}

.flex-around {
  display: flex;
  justify-content: space-around;
  align-items: center;
}

.flex-wrap {
  flex-wrap: wrap;
}

.align-start {
  align-items: flex-start;
}

.align-end {
  align-items: flex-end;
}

.align-stretch {
  align-items: stretch;
}

.calendar {
  background-color: #fff;
}

.calendar .title {
  font-size: 40rpx;
  color: #333;
  line-height: 60rpx;
  position: relative;
}

.calendar .title .year-month {
  margin-right: 20rpx;
}

.calendar .title .icon {
  padding: 0 16rpx;
  font-size: 32rpx;
  color: #999;
}

.calendar .title .open {
  background-color: #f6f6f6;
  color: #999;
  font-size: 22rpx;
  line-height: 36rpx;
  border-radius: 18rpx;
  padding: 0 14rpx;
  position: absolute;
  right: 40rpx;
}

.calendar .calendar-week {
  line-height: 40rpx;
  padding: 0 25rpx;
  font-size: 28rpx;
  color: #999;
}

.calendar .calendar-week .view {
  width: 100rpx;
  text-align: center;
}

.calendar .calendar-main {
  padding: 30rpx 25rpx;
  transition: height 0.3s;
  align-content: flex-start;
  overflow: hidden;
}

.calendar .calendar-main .day {
  position: relative;
  width: 100rpx;
  color: #666;
  text-align: center;
  height: 72rpx;
}

.calendar .calendar-main .day .bg {
  height: 56rpx;
  line-height: 56rpx;
  font-size: 28rpx;
  color: #333;
  font-weight: bold;
}

.calendar .calendar-main .day .select {
  width: 56rpx;
  border-radius: 50%;
  text-align: center;
  color: #fff;
  background: linear-gradient(-60deg, #65cc31, #65cc31);
  box-shadow: 0px 5px 16px 0px #c6f3ed;
  margin: 0 auto;
}

.calendar .calendar-main .day .other-month {
  color: #ccc;
}

.calendar .calendar-main .day .spot {
  width: 8rpx;
  height: 8rpx;
  background-color: #65cc31;
  border-radius: 50%;
  margin: 6rpx auto 0;
}

.calendar-week-clo {
  color: #65cc31;
}

.month-top {
  color: #b3b5a6;
}

/* 让当前日期时间显示为指定的文字 */
.today {
  color: rgb(119, 49, 49) !important;
}
.today::after {
  /* content: "今"; */
  color: red !important;
}

js

// components/calendar/calendar.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    spot: {
      type: Array,
      value: []
    },
    defaultTime: {
      type: String,
      value: ''
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    dateList: [], //日历主体渲染数组
    selectDay: {} //选中时间
  },

  /**
   * 组件的方法列表
   */
  methods: {
    formatTime(time, format) {
      function formatNumber(n) {
        n = n.toString()
        return n[1] ? n : '0' + n
      }

      function getDate(time, format) {
        const formateArr = ['Y', 'M', 'D', 'h', 'm', 's']
        const returnArr = []
        const date = new Date(time)
        returnArr.push(date.getFullYear())
        returnArr.push(formatNumber(date.getMonth() + 1))
        returnArr.push(formatNumber(date.getDate()))
        returnArr.push(formatNumber(date.getHours()))
        returnArr.push(formatNumber(date.getMinutes()))
        returnArr.push(formatNumber(date.getSeconds()))
        for (const i in returnArr) {
          format = format.replace(formateArr[i], returnArr[i])
        }
        return format
      }

      function getDateDiff(time) {
        let r = ''
        const ft = new Date(time)
        const nt = new Date()
        const nd = new Date(nt)
        nd.setHours(23)
        nd.setMinutes(59)
        nd.setSeconds(59)
        nd.setMilliseconds(999)
        const d = parseInt((nd - ft) / 86400000)
        switch (true) {
          case d === 0:
            const t = parseInt(nt / 1000) - parseInt(ft / 1000)
            switch (true) {
              case t < 60:
                r = '刚刚'
                break
              case t < 3600:
                r = parseInt(t / 60) + '分钟前'
                break
              default:
                r = parseInt(t / 3600) + '小时前'
            }
            break
          case d === 1:
            r = '昨天'
            break
          case d === 2:
            r = '前天'
            break
          case d > 2 && d < 30:
            r = d + '天前'
            break
          default:
            r = getDate(time, 'Y-M-D')
        }
        return r
      }
      if (!format) {
        return getDateDiff(time)
      } else {
        return getDate(time, format)
      }
    },
    //picker设置月份
    editMonth(e) {
      const arr = e.detail.value.split('-')
      const year = parseInt(arr[0])
      const month = parseInt(arr[1])
      this.setMonth(year, month)
    },
    //上月切换按钮点击
    lastMonth() {
      const lastMonth = new Date(
        this.data.selectDay.year,
        this.data.selectDay.month - 2
      )
      const year = lastMonth.getFullYear()
      const month = lastMonth.getMonth() + 1
      this.setMonth(year, month)
    },
    //下月切换按钮点击
    nextMonth() {
      const nextMonth = new Date(
        this.data.selectDay.year,
        this.data.selectDay.month
      )
      const year = nextMonth.getFullYear()
      const month = nextMonth.getMonth() + 1
      this.setMonth(year, month)
    },
    //设置月份
    setMonth(setYear, setMonth, setDay) {
      if (
        this.data.selectDay.year !== setYear ||
        this.data.selectDay.month !== setMonth
      ) {
        const day = Math.min(
          new Date(setYear, setMonth, 0).getDate(),
          this.data.selectDay.day
        )
        const time = new Date(setYear, setMonth - 1, setDay ? setDay : day)
        const data = {
          selectDay: {
            year: setYear,
            month: setMonth,
            day: setDay ? setDay : day,
            dateString: this.formatTime(time, 'Y-M-D')
          }
        }
        if (!setDay) {
          data.open = true
        }
        this.setData(data)
        this.dateInit(setYear, setMonth)
        this.setSpot()
        this.triggerEvent('change', this.data.selectDay)
      }
    },
    //展开收起
    openChange() {
      this.setData({
        open: !this.data.open
      })
      this.triggerEvent('aaa', { a: 0 })
      this.dateInit()
      this.setSpot()
    },
    //设置日历底下是否展示小圆点
    setSpot() {
      const timeArr = this.data.spot.map(item => {
        return this.formatTime(item, 'Y-M-D')
      })
      this.data.dateList.forEach(item => {
        if (timeArr.indexOf(item.dateString) !== -1) {
          item.spot = true
        } else {
          item.spot = false
        }
      })
      this.setData({
        dateList: this.data.dateList
      })
    },
    //日历主体的渲染方法
    dateInit(
      setYear = this.data.selectDay.year,
      setMonth = this.data.selectDay.month
    ) {
      let dateList = [] //需要遍历的日历数组数据
      let now = new Date(setYear, setMonth - 1) //当前月份的1号
      let startWeek = now.getDay() //目标月1号对应的星期
      let dayNum = new Date(setYear, setMonth, 0).getDate() //当前月有多少天
      let forNum = Math.ceil((startWeek + dayNum) / 7) * 7 //当前月跨越的周数
      if (this.data.open) {
        //展开状态,需要渲染完整的月份
        for (let i = 0; i < forNum; i++) {
          const now2 = new Date(now)
          now2.setDate(i - startWeek + 1)
          let obj = {}
          obj = {
            day: now2.getDate(),
            month: now2.getMonth() + 1,
            year: now2.getFullYear(),
            dateString: this.formatTime(now2, 'Y-M-D')
          }
          dateList[i] = obj
        }
      } else {
        //非展开状态,只需要渲染当前周
        for (let i = 0; i < 7; i++) {
          const now2 = new Date(now)
          //当前周的7天
          now2.setDate(
            Math.ceil((this.data.selectDay.day + startWeek) / 7) * 7 -
              6 -
              startWeek +
              i
          )
          let obj = {}
          obj = {
            day: now2.getDate(),
            month: now2.getMonth() + 1,
            year: now2.getFullYear(),
            dateString: this.formatTime(now2, 'Y-M-D')
          }
          dateList[i] = obj
        }
      }
      this.setData({
        dateList: dateList
      })
    },
    //一天被点击时
    selectChange(e) {
      const year = e.currentTarget.dataset.year
      const month = e.currentTarget.dataset.month
      const day = e.currentTarget.dataset.day
      const dateString = e.currentTarget.dataset.dateString
      const selectDay = {
        year: year,
        month: month,
        day: day,
        dateString: dateString
      }
      if (
        this.data.selectDay.year !== year ||
        this.data.selectDay.month !== month
      ) {
        this.setMonth(year, month, day)
      } else if (this.data.selectDay.day !== day) {
        this.setData({
          selectDay: selectDay
        })
        this.triggerEvent('change', this.data.selectDay)
      }
    },
    touchStart(e) {
      // console.log(e)
      this.setData({
        touchX: e.changedTouches[0].clientX,
        touchY: e.changedTouches[0].clientY
      })
    },
    touchEnd(e) {
      let x = e.changedTouches[0].clientX
      let y = e.changedTouches[0].clientY
      console.log()
      let touch = this.getTouchData(x, y, this.data.touchX, this.data.touchY)
      switch (touch) {
        case 'left':
          this.nextMonth()
          break
        case 'right':
          this.lastMonth()
          break

        default:
          break
      }
    },
    getTouchData(endX, endY, startX, startY) {
      let turn = ''
      if (endX - startX > 50 && Math.abs(endY - startY) < 50) {
        //右滑
        turn = 'right'
      } else if (endX - startX < -50 && Math.abs(endY - startY) < 50) {
        //左滑
        turn = 'left'
      }
      return turn
    }
  },
  lifetimes: {
    attached() {
      let now = this.data.defaultTime
        ? new Date(this.data.defaultTime)
        : new Date()
      let selectDay = {
        year: now.getFullYear(),
        month: now.getMonth() + 1,
        day: now.getDate(),
        dateString: this.formatTime(now, 'Y-M-D')
      }
      this.setMonth(selectDay.year, selectDay.month, selectDay.day)
    }
  },
  nextMonth: function (e) {
    let sRq = e.detail.value
    if (sRq == '2021/3/6') {
      //今天日期自己取
      this.setData({
        rq: '至今'
      })
    }
  },
  observers: {
    spot: function (spot) {
      this.setSpot()
    }
  }
})

使用页面代码

json
首先在json里面把你封装的组件拿过来

{
   "component": true,
   "usingComponents": {
      "calendar":"../../components/calendar/calendar" 
   },
   "disableScroll": true
}

wxml

<view class="main">
	<calendar bind:change="dateChange" spot="{{spot}}" />
	<view class="section">
		<image class="main-im" src="../../img/2.png" />
	</view>
	<view class="bottom" bindtap="nextpage">
		<!--<view>{{x.date === today?'今天':x.day}}</view>-->
		<image src="https://i.loli.net/2021/05/15/PIViWr1pLngxqyf.png" />
	</view>
</view>
<!-- <view class="date-string">当前选中的日期是:{{dateString}}</view> -->

wxss

/**index.wxss**/
page {
  background-color: #f6f6f6;
}

.date-string {
  margin-top: 20rpx;
  background-color: #fff;
  font-size: 28rpx;
  padding: 0 30rpx;
  line-height: 60rpx;
}

.main {
  width: 100%;
  height: 500px;
  font-size: 14px;
  position: relative;
  overflow-x: hidden;
  overflow-y: auto;
}

.bottom {
  position: fixed;
  bottom: 80rpx;
  right: 50rpx;
  z-index: 2001;
}

.bottom image {
  width: 120rpx;
  height: 125rpx;
}
.main-im {
  width: 260px;
  height: 260px;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: -1;
  margin: auto;
}

js

//index.js 调用封装的方法
Page({
  /**
   * 页面的初始数据
   */
  data: {
    dateString: "",
    spot: ['2021/1/17','2021/2/17','2021/3/17','2021/4/17','2021/5/17','2021/6/17','2021/7/17','2021/8/17','2021/9/17','2021/10/17','2021/11/17','2021/12/17'],
  },
  dateChange(e) {
    console.log("选中日期变了,现在日期是", e.detail.dateString)
    this.setData({
      dateString: e.detail.dateString
    })
  },
      // bindrqChange: function(e) {
      //     let sRq = e.detail.value
      //    if (sRq == '2021/3/5') { //今天日期自己取
      //     this.setData({
      //      rq: '今'
      //     })
      // }
      //  },
  onReady: function () {
  },
})

然后这边就是收缩展开左右滑动的日历效果
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值