这是我参与更文挑战的第4天,活动详情查看: 更文挑战javascript
这是使用UNIAPP
Uview
开发出来的可得计时小程序,是一款简约、方便的倒计时器微信小程序,您能够设置在上面设定将来的什么时候何分何秒后通知你。css
咱们先看看咱们的成品效果图,是否是以为很nice!html
在uniapp
中建立一个新的uniapp
项目,这里我就命名为codeTime了vue
咱们须要用到的uView
里边的CountDown
组件,因此咱们须要先安装uView
,在项目所在位置打开终端,执行 (由于咱们的项目是经过hbx建立的,没有package.json
文件)java
npm init -y
复制代码
npm install uview-ui
复制代码
安装以后,须要在项目中配置下(引入uView
框架)git
// main.js
import uView from "uview-ui";
Vue.use(uView);
复制代码
# uni.scss
@import 'uview-ui/theme.scss';
# app.vue
<style lang="scss">
@import "uview-ui/index.scss";
/*每一个页面公共css */
</style>
复制代码
// pages.json
"easycom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
},
复制代码
配置完后,咱们能够使用console.log(this.$u.config.v);
,查看版本,检测是否安装及配置成功github
onShow: function() {
console.log(this.$u.config.v);
},
复制代码
咱们还须要改下uView
框架中的CountDown
组件文件,由于须要咱们这个项目须要用到暂停倒计时,可是该组件没有提供改方法web
在CountDown
组件中的props
对象中加入属性npm
pause: {
type: Boolean,
default: false
}
复制代码
修改CountDown
组件中的start
方法,利用父组件传入的pause
属性,来判断是否执行seconds --
,也就便是否进行减秒操做json
// 倒计时
start() {
// 避免可能出现的倒计时重叠状况
this.clearTimer();
if (this.timestamp <= 0) return;
this.seconds = Number(this.timestamp);
this.formatTime(this.seconds);
this.timer = setInterval(() => {
if (!this.pause) {
this.seconds--;
// 发出change事件
this.$emit('change', this.seconds);
if (this.seconds < 0) {
return this.end();
}
this.formatTime(this.seconds);
} }, 1000);
},
复制代码
咱们把pages
文件夹下面的index.vue
文件整理下
<template>
</template>
<script>
export default {
data() {
return {
}
},
onLoad() {
},
methods: {
}
}
</script>
<style lang="scss">
</style>
复制代码
咱们项目组件主体分为三大部分
如今咱们就按这个顺序来书写咱们各部分的组件,到后面咱们将这些组件插入到index.vue中
note.vue
,其实它只是一个简单的input组件,没有任何逻辑处理的组件,也是这个项目中最简单的一个组件,没有之一哈哈
<template>
<view class="c-title">
<input class="c-input" type="text" :value="note" placeholder="#添加计时内容" placeholder-class="c-input-pla" />
</view>
</template>
复制代码
<script>
export default {
name:"note",
}
</script>
复制代码
<style lang="scss">
.c-title {
padding: 50rpx 20rpx 20rpx 20rpx;
margin: 20rpx;
color: #007aff;
width: 90%;
height: 60rpx;
.c-input {
text-align: center;
font-size: 45rpx;
line-height: 45rpx;
height: 60rpx;
}
.c-input-pla {
color: #e7e7e7;
}
}
</style>
复制代码
在index.vue
中引入它看看效果如何
<template>
<view class="c-container">
<note></note>
</view>
</template>
复制代码
<style lang="scss" scoped>
.c-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>
复制代码
这是一个时间选择器组件,也就是咱们用户选择时间用到的组件,这里用到的是uniapp
自带的组件,叫作picker-view
嵌入页面的滚动选择器,该组件文档地址 uniapp.dcloud.io/component/p…
<template>
<view class="u-picker-body">
<picker-view :value="valueArr" @change="change" class="u-picker-view" @pickstart="pickstart" @pickend="pickend">
<picker-view-column>
<view class="u-column-item" v-for="(item, index) in hours" :key="index">
{{ formatNumber(item) }}
<text class="c-text" v-if="showTimeTag">时</text>
</view>
</picker-view-column>
<picker-view-column>
<view class="u-column-item" v-for="(item, index) in minutes" :key="index">
{{ formatNumber(item) }}
<text class="c-text" v-if="showTimeTag">分</text>
</view>
</picker-view-column>
<picker-view-column>
<view class="u-column-item" v-for="(item, index) in seconds" :key="index">
{{ formatNumber(item) }}
<text class="c-text" v-if="showTimeTag">秒</text>
</view>
</picker-view-column>
</picker-view>
</view>
</template>
复制代码
<script>
export default {
name:"date-selector",
created(){
this.initTimeList()
},
data() {
return {
moving: false, // 列是否还在滑动中,微信小程序若是在滑动中就点肯定,结果可能不许确
hours: [],
minutes: [],
seconds: [],
hour: 0,
minute: 0,
second: 0,
valueArr: [],
timestamp:300
};
},
props:{
showTimeTag:{
type:Boolean,
default:false
}
},
methods:{
// 初始化
initTimeList(){
// 生成 时,分,秒
this.valueArr.push(0);
this.setHours();
this.valueArr.push(0);
this.setMinutes();
this.valueArr.push(0);
this.setSeconds();
},
getIndex(arr, val) {
let index = arr.indexOf(val);
// 若是index为-1(即找不到index值),~(-1)=-(-1)-1=0,致使条件不成立
return ~index ? index : 0;
},
// 小于10前面补0,用于月份,日期,时分秒等
formatNumber(num) {
return +num < 10 ? '0' + num : String(num);
},
// 标识滑动开始,只有微信小程序才有这样的事件
pickstart() {
// #ifdef MP-WEIXIN
this.moving = true;
// #endif
},
// 标识滑动结束
pickend() {
// #ifdef MP-WEIXIN
this.moving = false;
this.$emit('pickend',this.timestamp)
// #endif
},
setHours() {
this.hours = this.generateArray(0, 23);
this.valueArr.splice(this.valueArr.length - 1, 1, this.getIndex(this.hours, this.hour));
},
setMinutes() {
this.minutes = this.generateArray(0, 59);
this.valueArr.splice(this.valueArr.length - 1, 1, this.getIndex(this.minutes, this.minute));
},
setSeconds() {
this.seconds = this.generateArray(0, 59);
this.valueArr.splice(this.valueArr.length - 1, 1, this.getIndex(this.seconds, this.second));
},
// 生成递进的数组
generateArray(start, end) {
// 转为数值格式,不然用户给end-year等传递字符串值时,下面的end+1会致使字符串拼接,而不是相加
start = Number(start);
end = Number(end);
end = end > start ? end : start;
// 生成数组,获取其中的索引,并剪出来
return [...Array(end + 1).keys()].slice(start);
},
// 用户更改picker的列选项
change(e) {
this.valueArr = e.detail.value;
let i = 0;
this.hour = this.hours[this.valueArr[i++]];
this.minute = this.minutes[this.valueArr[i++]];
this.second = this.seconds[this.valueArr[i++]];
this.getTimeToSecond(this.hour, this.minute, this.second);
},
// 将picker的时间专成秒数
getTimeToSecond(hour, minute, second) {
let time = 0;
if (hour) {
time += hour * 60 * 60;
}
if (minute) {
time += minute * 60;
}
if (second) {
time += second;
}
this.timestamp = time;
},
quickSetMinutes(minutes){
this.valueArr[0] = 0;
this.valueArr[1] = minutes;
this.valueArr[2] = 0;
this.setHours()
this.setMinutes()
this.setSeconds()
}
}
}
</script>
复制代码
<style lang="scss">
.u-picker-body {
margin: 0;
width: 100%;
height: 500rpx;
overflow: hidden;
background-color: #ffffff;
.u-picker-view {
height: 100%;
width: 700rpx;
box-sizing: border-box;
margin: 0 auto;
.u-column-item {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
font-size: 50rpx;
color: #000000;
padding: 0 8rpx;
.c-text {
font-size: 24rpx;
padding-left: 8rpx;
}
}
}
}
</style>
复制代码
一样,咱们在index.vue
中引入它,在此以前,咱们还要引入咱们魔改后的CountDown
组件并传入须要的一些参数(timestamp
autoplay
...
),以后再引用咱们写好的dateSelector
组件,记得加上v-if
,当start
为true
时,说明当前正在倒计时,咱们须要把时间选择器隐藏起来。(其实这里使用v-show
更好,可是uniapp
自己不支持v-show
,由于v-if
会把元素从新渲染,这个以后再用style
的display
来控制吧,目前先这样子吧)
<template>
<view class="c-container">
<note></note>
<view class="c-countdown" v-if="start">
<u-count-down ref="uCountDown" :timestamp="timestamp" :autoplay="autoplay" :show-days="false" :font-size="100" :separatorSize="65" :pause="pause"></u-count-down>
</view>
<date-selector v-else ref="dateSelectorRef" showTimeTag @pickend="pickend"></date-selector>
</view>
</template>
复制代码
<script>
export default {
data() {
return {
start: false, // 是否开始倒计时
pause: false, // 是否处于暂停状态
autoplay: false,
timestamp: 300, // 默认五分钟
};
},
methods: {
pickend(timestamp) {
this.timestamp = timestamp
},
}
};
</script>
复制代码
整个项目的按钮组件都在这里边,它的按钮功能主要是控制倒计时器 CountDown
的的进行和暂停以及重置功能
<template>
<view class="c-controller">
<!-- 快捷设置按钮 -->
<view :animation="animationQuickChooseTime" class="c-default-list">
<view v-for="(time,index) in defaultTimeList" v-if="defaultTimeList.length!==0" @tap="selectDefaultTime(time,index)" :key="time">
<view :class="[index == selectIndex&&time*60==timestamp?'c-time-item-sel':'c-time-item']">
<view class="c-time-num">
{{time}}
</view>
<view :class="[index == selectIndex&&time*60==timestamp?'c-time-text-sel':'c-time-text']">
分钟
</view>
</view>
</view>
</view>
<!-- 控制按钮 -->
<view class="c-controller-area">
<view :animation="animationToLeftData" class="c-button" style="z-index: 1;" @tap="startCountTime()">
<image v-if="start" class="c-icon" src="../../static/restart.png"></image>
<image v-else class="c-icon" src="../../static/start.png"></image>
</view>
<view :animation="animationToRightData" class="c-button" @tap="stopCountTime()">
<image v-if="!pause" class="c-icon" src="../../static/stop.png"></image>
<image v-else class="c-icon" src="../../static/start.png"></image>
</view>
</view>
</view>
</template>
复制代码
<script>
export default {
name: "controller-area",
data() {
return {
// 动画
animationToLeftData: {},
animationToRightData: {},
animationQuickChooseTime: {},
selectIndex: null
};
},
methods: {
startCountTime() {
if (this.start) {
// 正在计时,中止它
this.$emit('startCountTime', this.start)
// 执行动画
let leftButton = uni.createAnimation({
transformOrigin: "50% 50%",
duration: 500,
timingFunction: "ease",
delay: 0
})
// 执行动画
let rightButton = uni.createAnimation({
transformOrigin: "50% 50%",
duration: 500,
timingFunction: "ease",
delay: 0
})
// 执行动画
let quickChooseTimeButton = uni.createAnimation({
transformOrigin: "50% 50%",
duration: 300,
timingFunction: "ease",
delay: 0
})
leftButton.translateX(0).step()
this.animationToLeftData = leftButton.export()
rightButton.translateX(0).opacity(0).step()
this.animationToRightData = rightButton.export()
quickChooseTimeButton.scale(1.1).step()
quickChooseTimeButton.scale(1).step()
this.animationQuickChooseTime = quickChooseTimeButton.export()
} else {
// 没有计时,开始计时
this.$emit('startCountTime', this.start)
// 执行动画
let leftButton = uni.createAnimation({
transformOrigin: "50% 50%",
duration: 500,
timingFunction: "ease",
delay: 0
})
// 执行动画
let rightButton = uni.createAnimation({
transformOrigin: "50% 50%",
duration: 500,
timingFunction: "ease",
delay: 0
})
// 执行动画
let quickChooseTimeButton = uni.createAnimation({
transformOrigin: "50% 50%",
duration: 500,
timingFunction: "ease",
delay: 0
})
leftButton.translateX(-60).step()
this.animationToLeftData = leftButton.export()
rightButton.opacity(1).translateX(60).step()
this.animationToRightData = rightButton.export()
quickChooseTimeButton.scale(0).step()
this.animationQuickChooseTime = quickChooseTimeButton.export()
}
},
stopCountTime() {
this.$emit('stopCountTime')
},
selectDefaultTime(time, index) {
this.$emit('clickSetTime', time)
this.selectIndex = index;
},
},
props: {
timestamp: {
type: Number,
default: 0
},
defaultTimeList: {
type: Array,
default: []
},
pause: {
type: Boolean,
default: false
},
start: {
type: Boolean,
default: false
}
}
}
</script>
复制代码
<style lang="scss">
.c-controller {
position: absolute;
top: 750rpx;
left: 0;
height: 400rpx;
width: 100%;
.c-default-list {
display: flex;
justify-content: center;
position: absolute;
top:0;
left: 0;
right: 0;
bottom: 0;
margin:auto;
.c-time-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 125rpx;
height: 125rpx;
border-radius: 50%;
padding: 20rpx;
margin: 20rpx;
background-color: #eeeeee;
}
.c-time-item-sel {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 125rpx;
height: 125rpx;
border-radius: 50%;
padding: 20rpx;
margin: 20rpx;
color: #FFFFFF;
background-color: #007aff;
}
.c-time-item,
.c-time-item-sel {
.c-time-num {
font-size: 45rpx;
}
.c-time-text {
font-size: 20rpx;
color: #808080;
}
.c-time-text-sel {
font-size: 20rpx;
color: #d0f0f7;
}
}
}
.c-controller-area {
height: 200rpx;
width: 100%;
position: absolute;
top: 200rpx;
left: 0;
margin: 0;
.c-button {
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 125rpx;
height: 125rpx;
border-radius: 50%;
background-color: #ffffff;
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.1), 0 3px 10px 0 rgba(0, 0, 0, 0.15);
top:0;
left: 0;
right: 0;
bottom: 0;
margin:auto;
/* 宽度的一半 */
.c-icon {
width: 50rpx;
height: 50rpx;
}
}
}
}
</style>
复制代码
继续在index.vue
中引入,在script
标签中,继续加入一些须要的属性和方法
<template>
<view class="c-container">
<note></note>
<view class="c-countdown" v-if="start">
<u-count-down ref="uCountDown" :timestamp="timestamp" :autoplay="autoplay" :show-days="false" :font-size="100" :separatorSize="65" :pause="pause"></u-count-down>
</view>
<date-selector v-else ref="dateSelectorRef" showTimeTag @pickend="pickend"></date-selector>
<controller-area @startCountTime="startCountTime" @stopCountTime="stopCountTime" @clickSetTime="clickSetTime" :defaultTimeList="defaultTime" :timestamp="timestamp" :pause="pause" :start="start" class="">
</controller-area>
</view>
</template>
复制代码
// 在index.vue中对应位置进行代码补充
data() {
return {
defaultTime: [5, 10, 15],
};
},
methods: {
// 开始倒计时
startCountTime() {
if (this.start) {
// 正在计时,中止它
console.log('restart');
this.start = false;
this.pause = false;
} else {
// 没有计时,开始计时
console.log('start');
this.start = true;
this.autoplay = true;
this.pause = false;
}
},
stopCountTime() {
this.pause = !this.pause;
},
clickSetTime(minutes) {
this.timestamp = minutes * 60;
this.$refs.dateSelectorRef.quickSetMinutes(minutes)
},
}
复制代码
到了这里咱们的三大组件已经书写好啦,接下来咱们就要在index.vue
文件中进行一个引用,和一些逻辑方法的书写
附上最终index.vue
的代码
<template>
<view class="c-container">
<note></note>
<view class="c-countdown" v-if="start">
<u-count-down ref="uCountDown" :timestamp="timestamp" :autoplay="autoplay" :show-days="false" :font-size="100" :separatorSize="65" :pause="pause"></u-count-down>
</view>
<date-selector v-else ref="dateSelectorRef" showTimeTag @pickend="pickend"></date-selector>
<controller-area @startCountTime="startCountTime" @stopCountTime="stopCountTime" @clickSetTime="clickSetTime" :defaultTimeList="defaultTime" :timestamp="timestamp" :pause="pause" :start="start" class="">
</controller-area>
</view>
</template>
复制代码
<script>
export default {
data() {
return {
start: false, // 是否开始倒计时
pause: false, // 是否处于暂停状态
autoplay: false,
timestamp: 300, // 默认五分钟
defaultTime: [5, 10, 15],
};
},
methods: {
// 开始倒计时
startCountTime() {
if (this.start) {
// 正在计时,中止它
console.log('restart');
this.start = false;
this.pause = false;
} else {
// 没有计时,开始计时
console.log('start');
this.start = true;
this.autoplay = true;
this.pause = false;
}
},
pickend(timestamp) {
this.timestamp = timestamp
},
stopCountTime() {
this.pause = !this.pause;
},
clickSetTime(minutes) {
this.timestamp = minutes * 60;
this.$refs.dateSelectorRef.quickSetMinutes(minutes)
},
}
};
</script>
复制代码
<style lang="scss" scoped>
.c-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.c-countdown {
margin-top: 50rpx;
}
</style>
复制代码
该小程序项目完整的GIT仓库地址:github.com/eatmans/cod…
别忘了 ⭐️⭐️⭐️
最后再贴个小程序码
感谢你们的阅读,但愿看完你们能有所收获! 若有不足之处,请多多指教!