web前端开发

April 19,2023【微信小程序】番茄时钟

2023-04-19  本文已影响0人  Du1in9
效果图1 效果图2

一、根目录

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
      })
    }
  }
})
view,sroll-view,swiper,icon,text,progress,button,checkbox,form,input,label,input,picker,radio,
slider,switch,action-sheet,modal,toast,loading,navigator,audio,image,video,map,canvas {
  box-sizing: border-box;
}
page {
  height: 100%;
}

.hide {
  display: none!important;
}

.container,input,button {
  font: 14px "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
}

.container {
  height: 100%;
  background-color: #f5f5f5;
}

.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;
}
{
  "pages": [
    "pages/index/index",
    "pages/logs/logs",
    "pages/setting/setting"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#3197ed",
    "navigationBarTitleText": "",
    "navigationBarTextStyle": "white"
  },
  "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/logs/logs",
      "iconPath": "image/wechat.png",
      "selectedIconPath": "image/wechatHL.png",
      "text": "记录"
    }, {
      "pagePath": "pages/setting/setting",
      "iconPath": "image/wechat.png",
      "selectedIconPath": "image/wechatHL.png",
      "text": "设置"
    }]
  }
}
{
    "description": "项目配置文件。",
    "setting": {
        "urlCheck": true,
        "es6": true,
        "postcss": true,
        "minified": true,
        "newFeature": true,
        "babelSetting": {
            "ignore": [],
            "disablePlugins": [],
            "outputPath": ""
        }
    },
    "compileType": "miniprogram",
    "libVersion": "2.31.0",
    "appid": "wxa5988aab181e5dec",
    "projectname": "timer",
    "condition": {},
    "packOptions": {
        "ignore": [],
        "include": []
    },
    "editorSetting": {
        "tabIndent": "insertSpaces",
        "tabSize": 4
    }
}

二、pages 目录

<view class="container timer {{isRuning ? 'timer--runing': ''}}">

    <!-- Part1:功能界面 -->
    <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>

    <!-- Part2:按钮界面 -->
    <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>
/******************************** A.运行前的属性设置 ********************************/

.container {                            /*总体布局:行展示,背景白色*/
  display: flex;                
  height: 100%;             
  flex-direction: column;
  overflow: hidden;
  background-color: #fff;
}

.timer_main {                           /*Part1:功能界面:内容居中,背景浅蓝色,过渡效果*/
  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 {                      /*计时圈布局:内容居中,宽高150,过渡效果*/
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 150px;
  height: 150px;
  text-align: center;
  transition: all .3s;
}

.timer_progress {                       /*计时半圈:宽75高150,过渡效果*/
  position: absolute;
  top: 0;
  width: 75px;
  height: 150px;
  overflow: hidden;
  transition: all .3s;
}

.timer_progress_mask {                  /*计时圈内环修饰:厚度3,透明度0.5*/
  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 {                           /*时长数字:白色,大小40,过渡效果*/
  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 {                      /*输入框:灰色,文字居中,距离底部40*/
  position: absolute;
  bottom: -40px;
  width: 100%;
  text-align: center;
  height: 40px;
  padding-left: 10px;
  border-bottom: 1px solid #f2f2f2;
  color: #999;
}

.timer_footer {                         /*Part2:按钮界面布局*/
  display: flex;
  justify-content: center;
  align-items: center;
  flex: 1;
  padding-top: 40px;
  transition: all .3s;
}

.timer .timer_ctrl {                    /*按钮属性:背景绿色,文字白色,宽高80,内容居中*/
  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;
}

/******************************** B.运行时的属性设置 ********************************/

.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 {            /*按钮属性:背景浅蓝,文字白色,高30*/
  background-color: #208DEA;
  height: 30px;
  margin-bottom: 30px;
  border: 1px dashed #dedede;
  border-radius: 20px;
}
const util = require('../../utils/util.js')
const defaultLogName = {work: '工作',rest: '休息'}
const actionName = {stop: '停止',start: '开始'}
const initDeg = {left: 45,right: -45,}

Page({

  data: {
    remainTimeText: '',
    timerType: 'work',
    log: {},
    completed: false,
    isRuning: false,
    leftDeg: initDeg.left,
    rightDeg: initDeg.right
  },

    onShow: function() {
        if (this.data.isRuning) return
        let workTime = util.formatTime(wx.getStorageSync('workTime'), 'HH')
        let restTime = util.formatTime(wx.getStorageSync('restTime'), 'HH')
        this.setData({
            workTime: workTime,
            restTime: restTime,
            remainTimeText: workTime + ':00'
        })
    },

    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)
    },

    startNameAnimation: function() {
        let animation = wx.createAnimation({duration: 450})
        animation.opacity(0.2).step()
        animation.opacity(1).step()
        this.setData({nameAnimation: animation.export()})
    },

    stopTimer: function() {
        // 重置计时进度
        this.setData({
            leftDeg: initDeg.left,
            rightDeg: initDeg.right
        })

        // 重置时间
        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 = util.formatTime(Math.floor(remainingTime / (60 * 60)) % 24, 'HH')
        let M = util.formatTime(Math.floor(remainingTime / (60)) % 60, 'MM')
        let S = util.formatTime(Math.floor(remainingTime) % 60, 'SS')
        let halfTime

        // 更新文本
        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
        }

        // 更新计时进度
        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)
            })
        }
    },

    changeLogName: function(e) {this.logName = e.detail.value},

    saveLog: function(log) {
        var logs = wx.getStorageSync('logs') || []
        logs.unshift(log)
        wx.setStorageSync('logs', logs)
    }
})
<!-- 情况1:有记录 -->
<block wx:if="{{logs.length}}">
    <!-- 任务记录界面 -->
    <scroll-view class="container" scroll-y="true">
        <view class="log panel">
            <view class="log_item" wx:for="{{logs}}" wx:for-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>
    <!-- 提示弹窗界面 -->
    <modal 
        title="提示" confirm-text="确定" cancel-text="取消" hidden="{{modalHidden}}" 
        bindconfirm="clearLog" bindcancel="switchModal"> 是否清除记录?此操作不可恢复!
    </modal>
</block>

<!-- 情况2:暂无记录 -->
<block wx:else>
    <view class="nodata">
        <image class="nodata_img" src="../../image/nodata.png"></image>
        <text class="nodata_text">暂无记录</text>
    </view>
</block>
<!-- 清楚成功的弹窗 -->
<toast hidden="{{toastHidden}}" bindchange="hideToast">清除成功!</toast>
.container{                 /*记录界面布置*/
    padding-bottom: 50px;
    max-height: 92%;
}
.log {                      /*文字属性设置*/
    color: #999;
    margin: 15px 10px;
}
.log_item{                  /*界面:上下距离15,左右距离5*/
    padding: 15px 5px;
}
.log_item text {            /*文字:左右距离20*/
    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%;
}
var util = require('../../utils/util.js')
Page({
  data: {
    logs: [],
    modalHidden: true,
    toastHidden: true
  },
  onShow: function() {
    wx.setNavigationBarTitle({
      title: '任务记录'
    })
    this.getLogs()
  },
  set: function() {

  },
  getLogs: function() {
    let logs = wx.getStorageSync('logs')
    logs.forEach(function(item, index, arry) {
      item.startTime = new Date(item.startTime).toLocaleString()
    })
    this.setData({
      logs: logs
    })
  },
  onLoad: function() {},
  switchModal: function() {
    this.setData({
      modalHidden: !this.data.modalHidden
    })
  },
  hideToast: function() {
    this.setData({
      toastHidden: true
    })
  },
  clearLog: function(e) {
    wx.setStorageSync('logs', [])
    this.switchModal()
    this.setData({
      toastHidden: false
    })
    this.getLogs()
  }
})
<view class="container">
    <!-- Part1:工作时长设置 -->
    <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>
    <!-- Part2:休息时长设置 -->
    <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>
    <!-- Part3:主页背景设置 -->
    <view class="section panel">
        <view class="section_title"><text>主页背景</text></view>
        <view class="section_body"><text bindtab="" class="section_tip">选择背景 > </text></view>
    </view>
    <!-- Part4:启用铃声设置 -->
    <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>
.container{                     /*总体布局设置*/
    padding:15px 10px;
}
.section{                       /*分区上下间距*/
    margin-bottom: 10px;
}
.section_title{                 /*标题属性设置*/
    font-size: 12px;
    color:rgb(153, 153, 153);
}
.section_check{                 /*勾选按钮位置*/
    margin-right: 5px;
}
.section_tip{                   /*背景、铃声分区属性设置*/
    display: block;
    text-align: right;
    color: #CEC9C9;
    padding-bottom: 10px;
}
.section_body{                  /*内容上下间距*/
    margin-top: 15px;
}
Page({
  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
    })
  },
  changeRestTime: function(e) {
    wx.setStorage({
        key: 'restTime',
        data: e.detail.value
    })
  }
})

三、utils 目录

function formatTime(time, format) {
  let temp = '0000000000' + time
  let len = format.length
  return temp.substr(-len)
}

module.exports = {
  formatTime: formatTime
}

四、image 目录

image/complete.png
image/nodata.png
image/wechat.png
image/wechatHL.png
上一篇 下一篇

猜你喜欢

热点阅读