参考链接:
(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:部门
- 小程序分享功能
小程序中分享的入口有两处,第一处是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) { }
}
}
- 打卡动画实现
效果如下,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(),
})
},
- 数组修改值
以一个示例介绍小程序中用setData方式修改数组的值。修改第0个元素为100。
Page({
data: {
tempArray: [1,2,3,4]
},
//事件处理函数
...
changeArray: function(){
var newArray= "tempArray[0]";
this.setData({
newArray :100
});
}
})
- 模态弹窗实现
微信小程序中目前没有提供较好的模态弹窗,很多也不适合项目的需求。如上图中,打卡规则的弹窗需要自己定义。
<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>
- 样式代码(部分)
/* 遮罩层 */
.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}}"> </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": "打卡日历"
}