用UNIAPP開發個倒計時小程序

這是我參與更文挑戰的第4天,活動詳情查看: 更文挑戰javascript

前言

這是使用UNIAPP Uview 開發出來的可得計時小程序,是一款簡約、方便的倒計時器微信小程序,您能夠設置在上面設定將來的什麼時候何分何秒後通知你。css

咱們先看看咱們的成品效果圖,是否是以爲很nicehtml

codetimeDome.gif

掃碼體驗下哦

掃碼_搜索聯合傳播樣式-微信標準綠版.png

接下來,讓咱們來實現它

uniapp中建立一個新的uniapp項目,這裏我就命名爲codeTimevue

1.png

咱們須要用到的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);
		},
複製代碼

2.png

咱們還須要改下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);
	},
複製代碼
CountDown組件說明文檔: www.uviewui.com/components/…

咱們把pages文件夾下面的index.vue文件整理下

<template>
	
</template>

<script>
	export default {
		data() {
			return {
				
			}
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style lang="scss">
	
</style>

複製代碼

咱們項目組件主體分爲三大部分

  • 筆記組件
    • note.vue
  • 時間選擇器組件
    • date-selector
  • 控制按鈕組件
    • controller-area

如今咱們就按這個順序來書寫咱們各部分的組件,到後面咱們將這些組件插入到index.vue中

note組件

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>
複製代碼

3.png

date-selector組件

這是一個時間選擇器組件,也就是咱們用戶選擇時間用到的組件,這裏用到的是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,當starttrue時,說明當前正在倒計時,咱們須要把時間選擇器隱藏起來。(其實這裏使用v-show更好,可是uniapp自己不支持v-show,由於v-if會把元素從新渲染,這個以後再用styledisplay來控制吧,目前先這樣子吧)

<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>
複製代碼

4.png

controller-area組件

整個項目的按鈕組件都在這裏邊,它的按鈕功能主要是控制倒計時器 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

附上最終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>
複製代碼

到這裏 終於寫完了!看看項目效果如何吧!

index.png

最後

該小程序項目完整的GIT倉庫地址:github.com/eatmans/cod…

別忘了 ⭐️⭐️⭐️

最後再貼個小程序碼

掃碼_搜索聯合傳播樣式-微信標準綠版.png

感謝你們的閱讀,但願看完你們能有所收穫! 若有不足之處,請多多指教!

相關文章
相關標籤/搜索