小程序开发日志-2、从零实现日历选择器
先看一下效果吧,毕竟不知道我写的标题表达得够不够明确。
有没有被帅到?标题说的日历查看器就是这个,那么下面我们来聊聊究竟该怎么实现这个强大好看(有点自恋)的日历查看(选择器)吧。
先具体说一下思路吧:
这个日历查看器其实最大的难点是怎么把月份和周的数据给对应上,其实利用数组很好解决这个问题,我们可以将月份的数据存入到数组中,日历表渲染就把这个数组遍历一下就好了。但在存入的时候,做一下小小的判断去给里面加几个空字符。那这个空字符是干嘛的呢?看下面一张图:
空字符对应的就是红色框框中的内容,图片日历表中的数组是这样子的:["","",1,2,3,......];一直到30号,30号后面就不必再加了。
上面就是基本的一个思路,下面看看应该怎么去做吧!
第一步,我们先创建一个静态的数据渲染先:
我把这个日历选择器分为两个部分,一个部分是操作那个部分,也就是有前进后退按钮那一个版块,另一个部分就是日历表,显示数据的部分。
先把第一部分实现,代码如下:
<!--日历控制器start--> <view class="hours-calendar flex-content border-top p-18"> <view class="flex-content remote-ctr"> <image src="/images/agent/pre-icon.png" bindtap="preYearFn"></image> <view class="margin-42 color-gray">{{years}}</view> <image src="/images/agent/next-icon.png" bindtap="nextYearFn"></image> </view> <view class="flex-content remote-ctr ml-27"> <image src="/images/agent/pre-icon.png" bindtap="preMonthFn"></image> <view class="margin-42 color-gray">{{months}}月</view> <image src="/images/agent/next-icon.png" bindtap="nextMonthFn"></image> </view> <view class="ml-auto back-btn" bindtap="backToFn"> 回到今天 </view> </view> <!--日历控制器end-->
上面是.wxml文件的部分,那些图片其实是左右箭头类的,最后会给大家贴一下,有需要的朋友可以自行下载喔!
接下来看看样式代码:
.hours-calendar{ padding: 35rpx 48rpx; background-color: #ffffff; } .remote-ctr image{ width: 19rpx; height: 35rpx; } .margin-42{ margin: 0 42rpx; } .ml-27{ margin-left: 27rpx; } .back-btn{ color: #ec6941; border-radius: 10rpx; border: 2rpx solid #ec6941; padding: 6rpx 21rpx; } .calendar-content{ padding-bottom: 30rpx; background-color: #ffffff; }
全局中公用的样式:
.flex-content{ display: flex; align-items: center; } .border-top{ border-top: 2rpx solid #e8e8e8; } .color-gray{ color: #a0a0a0; } .ml-auto{ margin-left: auto; }
实现第二部分日历表,同样利用强大的flex布局,然后让它去自动换行就ok了
<view class="calendar-content p-18 color-gray border-top"> <view class="flex-content week"> <view>一</view> <view>二</view> <view>三</view> <view>四</view> <view>五</view> <view>六</view> <view>日</view> </view> <view class="flex-wrap week"> <view data-day="{{item}}" wx:for="{{30}}" wx:key="key">{{item}}</view> </view> </view>
加上样式:
.calendar-content{ padding-bottom: 30rpx; background-color: #ffffff; } .week view{ width: 64rpx; height: 64rpx; text-align: center; line-height: 64rpx; margin-left: 38rpx; margin-top: 10rpx; } .is-today{ border-radius: 50%; background-color: #dfdfdf; position: relative; } .is-today::after{ content: "今天"; position: absolute; bottom: -20rpx; left: -3rpx; font-size: 12rpx; width: 70rpx; } .se-day{ border-radius: 50%; background-color: #ec6941; color: #ffffff; }
这样一个静态的日历表就出来了,效果如下:
有个0?这是数组遍历的惯性,大家知道就好了。
那么接下来就是我们的重点了,究竟怎么让周和月份对应上?
先创建一个日历js方法文件,calendar.js
然后贴出代码:
/** * 返回月份数组 * @param year * @param month * @returns {*[]} */ const getMonthList = function (year, month) { let date = new Date(); if(year){ date.setFullYear(year); date.setMonth(month - 1); date.setDate(1); let weekIndex = (date.getDay() - 1) < 0 ? 6 : (date.getDay() - 1); //获取本月总天数方法---将当前月份加1,下移到下一个月 console.log(month + ',设置的月份为:' + date.getMonth()); console.log('设置的礼拜为:' + weekIndex); date.setMonth(date.getMonth() + 1); //将当前日期置为0 date.setDate(0); console.log('获取的天数为:' + date.getDate()); //再获取天数即取上一个月的最后天数 let days = date.getDate(); let monthList =[]; //周几开始加上总天数,就是所生产数组的总个数 for(let i = 0; i < (weekIndex + days); i++){ if(weekIndex > i){ monthList.push(''); }else if((i) < (days + weekIndex + 1)){ monthList.push((i + 1) - weekIndex); }else { monthList.push(''); } } return monthList; } } module.exports = { getMonthList: getMonthList, }
上述的思路就跟开篇上将的是一样的,可以对照上面一起看。
有了这个方法之后,它可以根据我们传递的年份和月份,把一个带有空字符处理好的数组返回给我们。
然后我们稍稍修改一下原来那个遍历的wx:for那里的代码:
<view class="{{item === isToday ? 'is-today' : ''}} {{item === selectDay ? 'se-day' : ''}}" data-day="{{item}}" wx:for="{{calendarList}}" wx:key="key" bindtap="selectDayFn">{{item}}</view>
在生命周期函数加载的时候,自动获取本月的日历表:
/** * 生命周期函数--监听页面加载 */ onLoad: function (options) { this.setData({ groupId: Number(options.groupId), }); wx.setNavigationBarTitle({ title: options.name + '-工时' }); this.backToFn(); },
上面用到的backToFn其实就是一个回到今天的函数,不过是跟初始加载是一样的。
/** * 初始化当天日期数据 */ backToFn(){ let _date = new Date(); this.resetDataFn( _date.getFullYear(),_date.getMonth() + 1,_date.getDate()); },
什么?又包含了一个函数?这个resetDataFn是重点,下面会有四个函数会公用到,编程思想嘛,当然是能拆分就尽量拆分。
/** * 数据重置 * @param years 年份 * @param months 月份 * @param days 天 */ resetDataFn(years, months, days = this.data.isToday){ this.setData({ years: years, months: months, isToday: days, calendarList: calendar.getMonthList(years,months), }) },
数据忘记初始化了:
下面我们在小程序页面js文件中声明的几个变量:
data: { calendarList: [], years: 0, months: 0, isToday: 0, selectDay: 0, },
calendarList是日历表数组,years是年份,months是月份,isToday是天数,selectDay是被点击的天数(后面会用到)。
等等,似乎还忘记了什么东西?还没实现怎么选择呢?
变量声明好了,再来什么几个函数:Pages({})下。
/** * 上一年 */ preYearFn(){ let _date = new Date(); console.log(`${this.data.months},${_date.getMonth() + 1}`); let _days = this.data.years - 1 === _date.getFullYear() && (_date.getMonth() + 1) === this.data.months ? _date.getDate() : 0; this.resetDataFn(this.data.years - 1, this.data.months, _days); }, /** * 下一年 */ nextYearFn(){ let _date = new Date(); let _days = this.data.years + 1 === _date.getFullYear() && (_date.getMonth() + 1) === this.data.months ? _date.getDate() : 0; this.resetDataFn(this.data.years + 1, this.data.months, _days); }, /** * 上一月 */ preMonthFn(){ let _date = new Date(); let _months = this.data.months - 1 === 0 ? 12 : this.data.months - 1; let _years = this.data.months - 1 === 0 ? this.data.years - 1 : this.data.years; let _days = (_date.getMonth() + 1) === _months && this.data.years === _date.getFullYear() ? _date.getDate() : 0; this.resetDataFn(_years, _months, _days); }, /** * 下一个月 */ nextMonthFn(){ let _date = new Date(); let _months = this.data.months + 1 === 13 ? 1 : this.data.months + 1; let _years = this.data.months + 1 === 13 ? this.data.years + 1 : this.data.years; let _days = (_date.getMonth() + 1) === _months && this.data.years === _date.getFullYear() ? _date.getDate() : 0; this.resetDataFn(_years, _months, _days); },
上面就是对应了四个按钮:
就这样,一个强大霸气的日历选择查看器就完成了。