番茄时钟<微信开发者工具>
一、 需求分析
1.功能介绍
1.开始界面
1.1 开始界面的中央处有倒计时钟,分布在其下的为“工作”与“休息”的两个按钮,点下任一个按钮即可触发响应的倒计时进行计时工作。
1.2 在按钮下发有一个自动聚焦的输入框,在此处输入想要专注的任务,然后点击上方的按钮即可进行专注工作。
1.3最下方为选项卡,第一个对应的即为开始界面,第二个位记录界面,第三个为设置界面。选中图标会跳转到相应的界面同时图标会变成绿色。
2.记录界面
会显示“时间段+任务名“,时刻提醒自己做了些什么,底部正中央有个“清除记录”的选项,点击后会清空所有记录。
3.设置界面
工作时长和休息时长可调,最上方有两个滑块,滑动即可进行时间调节。
二、开始界面
一、页面设计
- 整理初始代码,删除部分文件及代码。
1.1 打开app.js文件,删除自带代码,只保留以下代码。
1.2 删除logs文件包。
1.3 打开app.json文件,删除"pages/logs/logs",保留以下代码。
{
"pages":[
"pages/index/index"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#3197ed",
"navigationBarTitleText": "",
"navigationBarTextStyle": "white"
}
}
1.4 删除index.wxml和index.wxss中所有代码。
1.5 打开index.js文件,删除自带代码,只保留以下代码。
//index.js
//获取应用实例
const app = getApp()
Page({
})
1.6新建如下图文件:
1、打开app.wxss文件,修改代码
/**app.wxss**/
view{
box-sizing: border-box;
}
page {
height: 100%;
}
.container,input,button {
font: 14px "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
}
.container {
height: 100%;
background-color: #f5f5f5;
}
2、打开app.json文件,修改代码
"tabBar": {
"color": "#dddddd",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "image/wechat.png",
"selectedIconPath": "image/wechatHL.png",
"text": "主页"
},
{
"pagePath": "pages/record/record",
"iconPath": "image/wechat.png",
"selectedIconPath": "image/wechatHL.png",
"text": "记录"
},
{
"pagePath": "pages/setting/setting",
"iconPath": "image/wechat.png",
"selectedIconPath": "image/wechatHL.png",
"text": "设置"
}
]
},
3、修改pages/index/index.wxml文件。
<view class="container timer {{isRuning ? 'timer--runing': ''}}">
<view class="timer_main">
<view class="timer_time-wrap">
<view class="timer_progress_mask"></view>
<view class="timer_progress timer_left">
<view class="timer_circle timer_circle--left" style="transform: rotate({{leftDeg}}deg);"></view>
</view>
<view class="timer_progress timer_right">
<view class="timer_circle timer_circle--right" style="transform: rotate({{rightDeg}}deg);"></view>
</view>
<text wx:if="{{!completed}}" class="timer_time">{{remainTimeText}}</text>
<text
wx:if="{{isRuning}}"
animation="{{nameAnimation}}"
class="timer_taskName">{{taskName}}{{completed ? '已完成!' : '中'}}</text>
<image wx:if="{{completed}}" class="timer_done" src="../../image/complete.png"></image>
</view>
<input
type="text"
placeholder-style="text-align:center"
class="timer_inputname"
bindinput="changeLogName"
placeholder="给您的任务取个名字吧"/>
</view>
<view class="timer_footer">
<view
bindtap="startTimer"
data-type="work"
class="timer_ctrl {{isRuning && timerType == 'rest' ? 'hide' : ''}}" >{{isRuning ? '完成': '工作'}}</view>
<view
bindtap="startTimer"
data-type="rest"
class="timer_ctrl {{isRuning && timerType == 'work' ? 'hide' : ''}}" >{{isRuning ? '完成': '休息'}}</view>
</view>
</view>
4、修改pages/index/index.wxss文件
/**index.wxss**/
.container {
display: flex;
height: 100%;
flex-direction: column;
overflow: hidden;
background-color: #fff;
}
.timer_main {
position: relative;
display: flex;
flex: 2;
justify-content: center;
align-items: center;
text-align: center;
background-color: #3197ed;
transition: all .5s;
z-index: 1;
padding: 10px 0;
}
.timer_time-wrap {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 150px;
height: 150px;
text-align: center;
transition: all .3s;
}
.timer_progress {
position: absolute;
top: 0;
width: 75px;
height: 150px;
overflow: hidden;
transition: all .3s;
}
.timer_progress_mask {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
border: 3px solid #1589eb;
opacity: .5;
border-radius: 50%;
}
.timer_left {
left: 0;
}
.timer_right {
right: 0;
}
.timer_circle {
position: absolute;
top: 0;
width: 150px;
height: 150px;
border: 3px solid transparent;
border-radius: 50%;
transition: all .3s;
}
.timer_circle--left {
left: 0;
border-left: 3px solid #fff;
border-bottom: 3px solid #fff;
transform: rotate(45deg);
}
.timer_circle--right {
right: 0;
border-right: 3px solid #fff;
border-bottom: 3px solid #fff;
transform: rotate(-45deg);
}
.timer_time {
font-size: 40px;
color: #fff;
font-weight: lighter;
transition: font-size .3s;
}
.timer_taskName {
position: absolute;
top: -100px;
font-size: 14px;
letter-spacing: 5px;
color: #fff;
}
.timer_done {
width: 64px;
height: 64px;
}
.timer_inputname {
position: absolute;
bottom: -40px;
width: 100%;
text-align: center;
height: 40px;
padding-left: 10px;
border-bottom: 1px solid #f2f2f2;
color: #999;
}
.timer_footer {
display: flex;
justify-content: center;
align-items: center;
flex: 1;
padding-top: 40px;
transition: all .3s;
}
.timer .timer_ctrl {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
font-size: 12px;
color: #fff;
width: 80px;
height: 80px;
margin: 0 20px;
border-radius: 50%;
transition: all .7s;
background-color: #48c23d;
}
.hide {
display: none!important;
}
/*runing style*/
.timer--runing .timer_main {
flex: 1;
}
.timer--runing .timer_time {
font-size: 45px;
}
.timer--runing .timer_time-wrap {
width: 200px;
height: 200px;
}
.timer--runing .timer_progress {
width: 100px;
height: 200px;
}
.timer--runing .timer_circle {
width: 200px;
height: 200px;
}
.timer--runing .timer_footer {
flex: 0;
position: absolute;
bottom: 0;
width: 100%;
z-index: 10;
}
.timer--runing .timer_ctrl {
background-color: #208DEA;
height: 30px;
margin-bottom: 30px;
border: 1px dashed #dedede;
border-radius: 20px;
}
二、逻辑层开发
1、打开app.js文件,添加如下代码
//app.js
// 设置全局变量
const defaultTime = {
defaultWorkTime: 25,
defaultRestTime: 5
}
App({
onLaunch: function() {
let workTime = wx.getStorageSync('workTime')
let restTime = wx.getStorageSync('restTime')
if (!workTime) {
wx.setStorage({
key: 'workTime',
data: defaultTime.defaultWorkTime
})
}
if (!restTime) {
wx.setStorage({
key: 'restTime',
data: defaultTime.defaultRestTime
})
}
}
})
2、打开index.js文件,添加代码如下
//index.js
//获取应用实例
const defaultLogName = {
work: '工作',
rest: '休息'
}
const actionName = {
stop: '停止',
start: '开始'
}
const initDeg = {
left: 45,
right: -45,
}
const app = getApp()
Page({
data: {
remainTimeText: '',//显示文字
timerType: 'work',//定时器类型
log: {}, //记录
completed: false,//是否已完成定时器
isRuning: false,//定时器是否在运行
leftDeg: initDeg.left,//左半圆进展弧度
rightDeg: initDeg.right//右半圆进展弧度
},
formatTime: function(time, format) {
let temp = '0000000000' + time
let len = format.length
return temp.substr(-len)
},
onShow: function() {
if (this.data.isRuning) return //如果计时器在运行,则直接不执行下面代码
//获取缓存中设置的时间信息
let workTime = this.formatTime(wx.getStorageSync('workTime'), 'HH')
let restTime = this.formatTime(wx.getStorageSync('restTime'), 'HH')
this.setData({
workTime: workTime,
restTime: restTime,
remainTimeText: workTime + ':00'
})
},
saveLog: function(log) {
var logs = wx.getStorageSync('logs') || []
logs.unshift(log)
wx.setStorageSync('logs', logs)
},
changeLogName: function(e) {
this.logName = e.detail.value
},
startNameAnimation: function() {
let animation = wx.createAnimation({
duration: 450
})
animation.opacity(0.2).step()
animation.opacity(1).step()
this.setData({
nameAnimation: animation.export()
})
},
stopTimer: function() {
// reset circle progress
this.setData({
leftDeg: initDeg.left,
rightDeg: initDeg.right
})
// clear timer
this.timer && clearInterval(this.timer)
},
updateTimer: function() {
let log = this.data.log
let now = Date.now()
let remainingTime = Math.round((log.endTime - now) / 1000)
let H = this.formatTime(Math.floor(remainingTime / (60 * 60)) % 24, 'HH')
let M = this.formatTime(Math.floor(remainingTime / (60)) % 60, 'MM')
let S = this.formatTime(Math.floor(remainingTime) % 60, 'SS')
let halfTime
// update text
if (remainingTime > 0) {
let remainTimeText = (H === "00" ? "" : (H + ":")) + M + ":" + S
this.setData({
remainTimeText: remainTimeText
})
} else if (remainingTime == 0) {
this.setData({
completed: true
})
this.stopTimer()
return
}
// update circle progress
halfTime = log.keepTime / 2
if ((remainingTime * 1000) > halfTime) {
this.setData({
leftDeg: initDeg.left - (180 * (now - log.startTime) / halfTime)
})
} else {
this.setData({
leftDeg: -135
})
this.setData({
rightDeg: initDeg.right - (180 * (now - (log.startTime + halfTime)) / halfTime)
})
}
},
startTimer: function(e) {
let startTime = Date.now()
let isRuning = this.data.isRuning
let timerType = e.target.dataset.type
let showTime = this.data[timerType + 'Time']
let keepTime = showTime * 60 * 1000
let logName = this.logName || defaultLogName[timerType]
if (!isRuning) {
this.timer = setInterval((function() {
this.updateTimer()
this.startNameAnimation()
}).bind(this), 1000)
} else {
this.stopTimer()
}
this.setData({
isRuning: !isRuning,
completed: false,
timerType: timerType,
remainTimeText: showTime + ':00',
taskName: logName
})
this.data.log = {
name: logName,
startTime: Date.now(),
keepTime: keepTime,
endTime: keepTime + startTime,
action: actionName[isRuning ? 'stop' : 'start'],
type: timerType
}
this.saveLog(this.data.log)
},
})
三、记录界面
一.页面设计
1、打开record.wxml文件,添加如下代码:
<!--pages/record/record.wxml-->
<block wx:if="{{logs.length}}">
<scroll-view class="container" scroll-y="true">
<view class="log panel">
<view class="log_item" wx:for="{{logs}}" wx:if-index="$index"wx:for-item="log">
<text class="log_start">{{log.startTime}}</text>
<text class="log_action">{{log.action}}</text>
<text class="log_action">{{log.name}}</text>
</view>
</view>
</scroll-view>
<view class="clear">
<button bindtap="switchModal" class="clear_btn" size="mini">清除记录</button>
</view>
</block>
<block wx:else>
<view class="nodata">
<image class="nodata_img" src="../../image/nodata.png"></image>
<text class="nodata_text">暂无记录</text>
</view>
</block>
2、打开record.wxss文件,添加如下代码:
/* pages/record/record.wxss */
.container{
padding-bottom: 50px;
max-height: 92%;
}
.log{
color: #999;
margin: 15px 10px;
}
.log_item{
padding:15px 5px;
}
.log_item text{
padding-right: 20px;
}
.clear{
position: absolute;
bottom: 0px;
width: 100%;
text-align: center;
}
.clear.clear_btn{
margin: 10px;
height: 35px;
line-height: 35px;
color: #fff;
background-color: #fd8f58;
border-radius: 2px;
width: 80%;
}
.panel{
background:#fff ;
border-radius: 3px;
padding: 5px 10px;
}
.nodata{
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.nodata_img{
display: block;
width: 45px;
height: 45px;
}
.nodata_text{
color: #dedede;
font-size: 12px;
padding-left: 10px;
}
3、打开record.js文件,添加如下代码:
// pages/record/record.js
Page({
/**
* 页面的初始数据
*/
data: {
logs: []
},
getLogs: function() {
let logs = wx.getStorageSync('logs')
logs.forEach(function(item,index,arry) {
item.startTime = new Date(item.startTime).toLocaleString()
})
this.setData({
logs: logs
})
},
clearLog: function(e) {
wx.setStorageSync('logs', [])
this.getLogs()
wx.showToast({
title: '清除成功',
icon: 'success',
duration: 2000
})
},
switchModal: function() {
wx.showModal({
title: '提示',
content: '是否清除记录?此操作不可恢复!',
success:(res)=> {
if (res.confirm) {
console.log('用户点击确定')
this.clearLog();
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
wx.setNavigationBarTitle({
title: '任务记录'
})
this.getLogs()
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
四、设置界面
一.页面设计
1、打开setting.wxml文件,添加如下代码:
<!--pages/setting/setting.wxml-->
<view class="container">
<view class="section panel">
<text class="section_title">工作时长(分钟)</text>
<view class="section_body">
<slider bindchange="changeWorkTime" show-value="true" min="1" max="60" value="{{workTime}}" left-icon="cancel" right-icon="success_no_circle"/>
</view>
</view>
<view class="section panel">
<text class="section_title">休息时长(分钟)</text>
<view class="section_body">
<slider bindchange="changeRestTime" show-value="true" min="5" max="60" value="{{restTime}}" left-icon="cancel" right-icon="success_no_circle"/>
</view>
</view>
<view class="section panel">
<view class="section_title">
<text>主页背景</text>
</view>
<view class="section_body">
<text bindtab="" class="section_tip">选择背景></text>
</view>
</view>
<view class="section panel">
<view class="section_title">
<switch class="section_check" type="checkbox" size="mini" checked bindchange="switch1Change"/>
<text>启用铃声</text>
</view>
<view class="section_body">
<text bindtab="" class="section_tip">选择铃声></text>
</view>
</view>
</view>
2、打开setting.wxss文件,添加如下代码:
/* pages/setting/setting.wxss */
.container{
padding-left: 15px 10px;
}
.section{
margin-bottom: 10px;
}
.section_title{
font-size: 12px;
color: #999;
}
.section_check{
margin-right: 5px;
}
.section_tip{
display: block;
text-align: right;
color: #CEC9C9;
padding-bottom: 10px;
}
.section_body{
margin-top: 15px;
}
.panel{
background: #fff;
border-radius: 3px;
padding: 5px 10px;
}
3、打开setting.js文件,添加如下代码:
// pages/setting/setting.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
wx.setNavigationBarTitle({
title: '设置'
})
this.setData({
workTime: wx.getStorageSync('workTime'),
restTime: wx.getStorageSync('restTime')
})
},
changeWorkTime:function(e){
wx.setStorage({
key: 'workTime',
data: 'e.detail.value',
})
},
changeWorkTime: function(e) {
wx.setStorage({
key: 'workTime',
data: e.detail.value
})
},
changeRestTime: function(e) {
wx.setStorage({
key: 'restTime',
data: e.detail.value
})
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
五、项目所需顺序图片
1.complete.png
2.nodata.png
3.view.gif