微信小程序 实现打卡功能

参考链接:
(1)微信小程序实现签到弹窗动画
https://www.jb51.net/article/196080.htm
(2)微信小程序开发(四)入门之打卡功能开发
https://blog.csdn.net/qq_17470165/article/details/80382761
【联动作者文章】
微信小程序开发(二)入门之日历打卡小程序发现页
https://blog.csdn.net/qq_17470165/article/details/80202743
微信小程序开发(三)入门之创建打卡活动
https://blog.csdn.net/qq_17470165/article/details/80215111
(3)微信小程序之打卡日历
https://blog.csdn.net/qq_38137411/article/details/81319299

一、例子1:签到弹窗打卡

在这里插入图片描述
wxml文件

<button bindtap="popup">签到</button>
<view class="load" style="{{sign?'':'top:calc((100vh - 750rpx)/2);opacity: 0;z-index:-1;'}}">
	 <image class="loadingpic" src="/images/light.png"></image>
	 <image class="load-imagae" src="/images/register.png"></image>
	 <view class="load-centent">
		 <view>签到成功</view>
		 <view>获得0积分</view>
		 <view bindtap="popup">好的</view>
	 </view>
</view>

wxss文件

page{
 background-color: #ccc;
}
view{
 box-sizing: border-box;
}
button{
 margin-top: 50rpx;
}
.load{
 width: 80%;
 height: 600rpx;
 position: fixed;
 top:calc((100vh - 800rpx)/2);
 left: 10%;
 transition: all 0.3s ease-in-out 0s;
 -webkit-transition: all 0.3s ease-in-out 0s;
 opacity: 1;
 z-index: 10;
}
.loadingpic {
 width: 100%;
 height: 93%;
 position: absolute;
 animation: load 3s linear 1s infinite;
 z-index: 10;
 margin-top: 7%;
}
@keyframes load{
 0%{
 webkit-transform: rotate(0deg);
 transform: rotate(0deg);
 }
 100%{
 webkit-transform: rotate(360deg);
 transform: rotate(360deg);
 }
}
.load-imagae{
 width:400rpx;
 height:400rpx;
 margin: 100rpx calc((100% - 400rpx)/2);
 position: absolute;
 z-index: 11;
}
.load-centent{
 width:400rpx;
 height:400rpx;
 margin: 100rpx calc((100% - 400rpx)/2);
 position: relative;
 z-index: 12;
 text-align: center;
 padding: 25% 10% 5% 10%;
}
.load-centent>view:nth-child(1){
 font-size: 34rpx;
 color: #fff;
}
.load-centent>view:nth-child(2){
 color: #fff;
 opacity: 0.7;
 margin: 20rpx 0;
 font-size: 24rpx;
}
.load-centent>view:nth-child(3){
 width: 85%;
 margin: 30rpx 7.5% 0 7.5%;
 height: 70rpx;
 line-height: 70rpx;
 border-radius: 40rpx;
 background-color: #F8D168;
 color: #EB4331;
 font-size: 30rpx;
}

js文件

Page({
	 data: {
	 sign:false,
	 },
	 popup:function(e){
		 this.setData({
		  sign:!this.data.sign
		 })
	 }, 
})

二、例子2:部门

在这里插入图片描述

  1. 小程序分享功能
    小程序中分享的入口有两处,第一处是menu:右上角转发菜单( 右上角…),第二处是页面内通过 转发。下面看下打卡页面右上角分享功能的实现。
 <view class='share-layout'>
    <image class='detail-cover' src='{{dakaPic}}' mode='aspectFill'></image>
    <view>
      <image class='share-icon' bindtap='shareSign' src='../../imgs/share_icon.png'></image>
      <button class='share-button' open-type='share' plain='true'></button>
    </view>
  </view>
.share-layout {
  position: relative;
  width: 100%;
}
.detail-cover {
  width: 100%;
  height: 312rpx;
}
.share-icon{
  position: absolute;
  right: 30rpx;
  top: 30rpx;
  width: 70rpx;
  height: 70rpx;
}
.share-button {
  z-index: 100;
  border: 0;
  opacity: 0;
  position: absolute;
  right: 30rpx;
  top: 30rpx;
  width: 70rpx;
  height: 70rpx;
}

实现方式很简单,上面说到小程序在页面内分享只能通过 标签通过添加open-type='share’来实现,将 层叠在图片上,设置opcity为0即可。当用户做分享操作会触发下面的方法,用户进入时onLoad()的options参数会携带分享的参数。

 /**
   * 用户点击右分享操作
   */
  onShareAppMessage: function () {
    var that = this;
    return {        
   		// 分享标题   
   		title: this.data.dakaName,                 
   		//分享路径,用户点击微信中分享卡片进入小程序的页面,以及携带的参数
   		path: 'pages/signdetail/notjoin?userId' + wx.getStorageSync('userId') 
                                           		+ "&activityId=" + this.data.activityId,                       
   		success: function (res) {
      		that.shareCallback();                //此处为了记录分享次数
    	},
    	fail: function (res) { } 
    }
}
  1. 打卡动画实现
    效果如下,GIF图有点卡顿。
    在这里插入图片描述
<image class=='sign-icon' animation='{{animationData}}' bindtap='signClick' src='../../imgs/sign_button.png'></image>

首先,要在我们实现动画的组件上添加’ animation’属性,下面看下animationData是如何创建的。

signClick: function () {
    var that = this
    //第1步:创建动画实例
    var animation = wx.createAnimation({
      duration: 1000,                     //动画时长   
      timingFunction: "ease",             //线性 
      delay: 0                           //0则不延迟 
    })
    //第2步:将创建的动画实例赋值给当前的动画
    this.animation = animation;
    //第3步:执行动画,Z轴旋转360度
    animation.opacity(1).rotateZ(360).step();
    //第4步:导出动画对象赋值给数据对象
    this.setData({
      animationData: animation.export(),
    })
    //设置指定时间后进行页面跳转
    setTimeout(function () {
      wx.navigateTo({
        url: '../sign/sign?isMessage=' + this.data.isMessage
        + "&actId=" + this.data.activityId
        + "&nickName=" + this.data.nickName,
      })
    }.bind(this), 800)
  },

这里在开发时遇到一个问题,跳转到下一个页面没有做任何操作,返回当前页面,当点击打卡操作,此时旋转动画将不会再执行。按照正常逻辑来说,我每次点击打卡按钮相当于重新创建动画实例执行操作,然而效果看起来并不是。这个动画实例除非页面销毁才会重置,否则一直是动画执行结束后的状态。这里我在页面隐藏的时候将动画重置为初始状态。

 /**
 * 生命周期函数--监听页面隐藏
 */
  onHide: function () {
    var animation = wx.createAnimation({
      duration: 10,
      timingFunction: "ease", //线性 
      delay: 0, //0则不延迟 
    })
    this.animation = animation;
    //重置动画为原始状态,将原始状态赋值给数据对象进行保存
    animation.opacity(1).rotateZ(0).step();
    this.setData({
      animationData: animation.export(),
    })
  },
  1. 数组修改值
    以一个示例介绍小程序中用setData方式修改数组的值。修改第0个元素为100。
Page({
  data: {
    tempArray: [1,2,3,4]
  },
  //事件处理函数
  ...
  changeArray: function(){
    var newArray= "tempArray[0]";
    this.setData({    
          newArray :100
        });
    }
})
  1. 模态弹窗实现
    微信小程序中目前没有提供较好的模态弹窗,很多也不适合项目的需求。如上图中,打卡规则的弹窗需要自己定义。
<view class="root-layout">
  <!-- 遮罩层,全屏显示,层级 z-index=1000  -->
  <view class="shade-mask" bindtap="shadeMaskHandler" data-status="close" wx:if="{{showModalStatus}}"/>
  <!-- 模态弹窗,显示打卡规则 层级 z-index = 10001 -->
  <view animation="{{animationData}}" class="model-box" wx:if="{{showModalStatus}}">
    <text class="rule-title">打卡规则</text>
    <view class="rule-content-layout">
      <block wx:for="{{dakaRules}}" wx:for-item="ruleItem">
        <view class="rule_content">
          <view class="dot"></view>
          <text class="rule">{{ruleItem}}</text>
        </view>
      </block>
    </view>
    <!-- 确定按钮 -->
    <text class="btn-sure" bindtap="shadeMaskHandler" data-status="close">确定</text>
  </view>
</view>
  1. 样式代码(部分)
/* 遮罩层 */
.shade-mask {
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  background: #000;
  opacity: 0.5;
  overflow: hidden;
}
/* 模态弹窗 */
.model-box {
  width: 600rpx;
  overflow: hidden;
  position: fixed;
  top: 50%;
  left: 25rpx;
  z-index: 1001;
  background: #fafafa;
  margin: -150px 50rpx 0 50rpx;
  border-radius: 6rpx;
}
/* 确定按钮 */
.btn-sure {
  display: block;
  padding: 30rpx;
  font-size: 32rpx;
  text-align: center;
  border-top: 1rpx solid #e8e8ea;
  color: #7885e8;
}

三、例子3:日历打卡

在这里插入图片描述
Calendar.wxml 页面文件
页面上显示出来的东西,布局上主要是一个年月栏、上一个月和下一个月的按钮;然后是星期栏,就是日一二三四五六,然后就是每个月的日期,注意每个月的前面可能有空的地方。这里面用wx:if标签来区分当前日期有无打卡的情况。

<!--pages/Calendar/Calendar.wxml-->
<!-- 打卡日历页面 -->
<view class='all'>
  <view class="bar">
    <!-- 上一个月 -->
    <view class="previous" bindtap="handleCalendar" data-handle="prev">
      <image src='../../images/pre.png'></image>
    </view>
    <!-- 显示年月 -->
    <view class="date">{{cur_year || "--"}}{{cur_month || "--"}}</view>
    <!-- 下一个月 -->
    <view class="next" bindtap="handleCalendar" data-handle="next">
      <image src='../../images/next.png'></image>
    </view>
  </view>
  <!-- 显示星期 -->
  <view class="week">
    <view wx:for="{{weeks_ch}}" wx:key="{{index}}" data-idx="{{index}}">{{item}}</view>
  </view>
  <view class='days'>
    <!-- 列 -->
    <view class="columns" wx:for="{{days.length/7}}" wx:for-index="i" wx:key="i">
      <view wx:for="{{days}}" wx:for-index="j" wx:key="j">
        <!-- 行 -->
        <view class="rows" wx:if="{{j/7 == i}}">
          <view class="rows" wx:for="{{7}}" wx:for-index="k" wx:key="k">
            <!-- 每个月份的空的单元格 -->
            <view class='cell' wx:if="{{days[j+k].date == null}}">
              <text decode="{{true}}">&nbsp;&nbsp;</text>
            </view>
            <!-- 每个月份的有数字的单元格 -->
            <view class='cell' wx:else>
              <!-- 当前日期已签到 -->
              <view wx:if="{{days[j+k].isSign == true}}" style='background-color:#83C75D' class='cell'>
                <text>{{days[j+k].date}}</text>
              </view>
              <!-- 当前日期未签到 -->
              <view wx:else>
                <text>{{days[j+k].date}}</text>
              </view>
            </view>
          </view>
        </view>
      </view>
    </view>
  </view>
  <!-- 坚持打卡天数 -->
  <view class='count'>
    <text>截至目前,你已坚持打卡</text>
    <view class='daynumber'>
    <text class='number'>{{count}}</text>
    <text class='day'></text>
    </view>    
    <text>请再接再厉,继续努力</text>
  </view>
</view>

Calendar.wxss 样式文件
这个就是让页面显示得更好看一点了,里面有些属性更改之后可能会导致整个页面的格式变得很乱,说明自己的功夫还是不到家。

/* pages/Calendar/Calendar.wxss */
/* 打卡日历 */
.all{
  margin-top: 20rpx;
}

.all .bar{
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  margin: 30rpx 20rpx;
  padding: 10rpx;
}

.all .bar image{
  width: 50rpx;
  height: 50rpx;
}

.all .week{
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 20rpx;
  padding-left: 40rpx;
  padding-right: 40rpx;
  margin: 20rpx;
  border-radius: 10px;
  background-color: #acd;
}

.all .days{
  margin: 20rpx;
  padding: 10rpx;
  border-radius: 10px;
  background-color: #acd;

}

.all .columns{
  display: flex;
  flex-direction: column;
  justify-content: space-between; 
}

.all .columns .rows{
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}

.all .columns .rows .cell{
  width: 84rpx;
  height: 88rpx;
  margin: 3rpx;
  text-align: center;
  border-radius: 50%;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.count .daynumber{
  display: flex;
  flex-direction: row;
  justify-content: center;
}

.count .daynumber .day{
  margin-top: 50rpx;
}

.count{
  margin: 20rpx;
  padding: 30rpx;
  display: flex;
  text-align: center;
  border-radius: 10px;
  flex-direction: column;
  justify-content: center;
  background-color: #acd;
  align-items: center;
}

.count .number{
  color: red;
  font-size: 60rpx;
  background-color: #fff;
  width: 100rpx;
  height: 100rpx;
  border-radius: 50%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin: 20rpx;
}

.count text{
  margin: 10rpx;
}

Calendar.js javaScript文件
js文件里面涉及到Bmob的操作,这里就不多说Bmob的操作了,感兴趣的同学可以去参考它的官方文档。
然后里面主要是对上一个月、下一个月的点击函数进行处理,以及对某年某月的每个日期进行初始化(尤其是每个月前的可能有的几个空格进行了处理),然后就是判断某个日期在后台数据中是否有打卡。

// pages/Calendar/Calendar.js
//打卡日历页面
var util = require('../../utils/util.js');
var Bmob = require('../../utils/bmob.js');
Page({

  /**
   * 页面的初始数据
   */
  data: {
    objectId:'',
    days:[],
    signUp:[],
    cur_year:0,
    cur_month:0,
    count:0
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    this.setData({objectId : options.objectId}); 
    //获取当前年月  
    const date = new Date();
    const cur_year = date.getFullYear();
    const cur_month = date.getMonth() + 1;
    const weeks_ch = ['日', '一', '二', '三', '四', '五', '六'];
    this.calculateEmptyGrids(cur_year, cur_month);
    this.calculateDays(cur_year, cur_month);
    //获取当前用户当前任务的签到状态
    this.onGetSignUp();
    this.setData({
      cur_year,
      cur_month,
      weeks_ch
    })
  
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {
  
  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {
  
  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {
  
  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {
  
  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {
  
  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {
  
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {
  
  },
  // 获取当月共多少天
  getThisMonthDays:function(year, month){
      return new Date(year, month, 0).getDate()
  },
    
  // 获取当月第一天星期几
  getFirstDayOfWeek:function(year, month) {
    return new Date(Date.UTC(year, month - 1, 1)).getDay();
  },

  // 计算当月1号前空了几个格子,把它填充在days数组的前面
  calculateEmptyGrids:function(year, month) {
    var that = this;
    //计算每个月时要清零
    that.setData({days:[]});
    const firstDayOfWeek = this.getFirstDayOfWeek(year, month);    
    if (firstDayOfWeek > 0) {
      for (let i = 0; i < firstDayOfWeek; i++) {
        var obj  = {
          date:null,
          isSign:false
        }
        that.data.days.push(obj);
      }
      this.setData({
        days:that.data.days
      });
    //清空
    } else {
      this.setData({
        days: []
      });
    }
  },

  // 绘制当月天数占的格子,并把它放到days数组中
  calculateDays:function(year, month) {
    var that = this;
    const thisMonthDays = this.getThisMonthDays(year, month);
    for (let i = 1; i <= thisMonthDays; i++) {
      var obj = {
        date: i,
        isSign: false
      }
      that.data.days.push(obj);
    }
    this.setData({
      days:that.data.days
    });
  },

  //匹配判断当月与当月哪些日子签到打卡
  onJudgeSign:function(){
    var that = this;
    var signs = that.data.signUp;
    var daysArr = that.data.days;
    for (var i=0; i < signs.length;i++){
      var current = new Date(signs[i].date.replace(/-/g, "/"));
      var year = current.getFullYear();
      var month = current.getMonth()+1;
      var day = current.getDate();
      day = parseInt(day);
      for (var j = 0; j < daysArr.length;j++){
        //年月日相同并且已打卡
        if (year == that.data.cur_year && month == that.data.cur_month && daysArr[j].date == day && signs[i].isSign == "今日已打卡"){
          daysArr[j].isSign = true;
        }
      }
    }
    that.setData({days:daysArr});
  },

  // 切换控制年月,上一个月,下一个月
  handleCalendar:function(e) {
    const handle = e.currentTarget.dataset.handle;
    const cur_year = this.data.cur_year;
    const cur_month = this.data.cur_month;
    if (handle === 'prev') {
      let newMonth = cur_month - 1;
      let newYear = cur_year;
      if (newMonth < 1) {
        newYear = cur_year - 1;
        newMonth = 12;
      }
      this.calculateEmptyGrids(newYear, newMonth);
      this.calculateDays(newYear, newMonth);
      this.onGetSignUp();      
      this.setData({
        cur_year: newYear,
        cur_month: newMonth
      })
    } else {
      let newMonth = cur_month + 1;
      let newYear = cur_year;
      if (newMonth > 12) {
        newYear = cur_year + 1;
        newMonth = 1;
      }
      this.calculateEmptyGrids(newYear, newMonth);
      this.calculateDays(newYear, newMonth);
      this.onGetSignUp();      
      this.setData({
        cur_year: newYear,
        cur_month: newMonth
      })
    }
  },

  //获取当前用户该任务的签到数组
  onGetSignUp:function(){
    var that = this;
    var Task_User = Bmob.Object.extend("task_user");
    var q = new Bmob.Query(Task_User);
    q.get(that.data.objectId, {
      success: function (result) {
        that.setData({
          signUp : result.get("signUp"),
          count : result.get("score")
        });
        //获取后就判断签到情况
        that.onJudgeSign();
      },
      error: function (object, error) {
      }
    });   
  }
})

Calendar.json json文件
这里仅仅是改变了导航栏上的标题文字。

{
  "navigationBarTitleText": "打卡日历"
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值