创建CountDownComponent
ionic g component count-down
运行完成后会生成如下目录
编写CountDownComponent
首先分析一下倒计时组件需要显示哪些元素,每个元素都需要在TS文件中定于对应变量:
- 剩余天数(day)
- 剩余小时数(hour)
- 剩余分钟数(minute)
- 剩余秒数(second)
- 当前状态(未开始,已开始未结束,已结束;timerType)
编写简单页面
<p>
开始时间:{{_startTime}}
</p>
<p>
结束时间:{{_endTime}}
</p>
<p>
<span *ngIf="showCountDown">还有{{timeDay}}天{{timeHour}}小时{{timeMinute}}分{{timeSecond}}秒</span> {{timerType}}
</p>
由于页面中使用到了ionic和angular提供的组件及指令等(如ngIf),所以需要修改components.module.ts*为如下 (添加IonicPageModule.forChild(CountDownComponent)):
@NgModule({
declarations: [CountDownComponent],
imports: [IonicPageModule.forChild(CountDownComponent)],
exports: [CountDownComponent]
})
export class ComponentsModule {
}
定义成员变量
/**
* 倒计时剩余天数
*/
timeDay: number = 0;
/**
* 倒计时剩余小时数
*/
timeHour: number = 0;
/**
* 倒计时剩余分钟数
*/
timeMinute: number = 0;
/**
* 倒计时剩余秒数
*/
timeSecond: number = 0;
/**
* 倒计时类型 ,未开始,一开始未结束,已结束
*/
timerType: string;
获取输入变量
我们现在已经定义了需要在HTML中显示的成员变量,但是这些成员变量都还没有对应的值,这些值需要从哪里来呢?他需要通过父组件(即调用当前组件的页面或组件)传入倒计时开始时间(startTime)与结束时间(endTime)来获取。
前面已经讲过,如果我们需要对传入的变量进行解析或者自定义处理,我们需要在set方法上使用@Input()。
@Input()
set startTime(value: Date) {
this._startTime = value;
}
@Input()
set endTime(value: Date) {
this._endTime = value;
}
解析输入变量
现在,我们已经有了所需要的两个时间,我们需要解析这两个时间来获取包括倒计时的状态等信息。同时,我们考虑到倒计时有可能不是一加载这个组件之后就启动,所以我们把解析输入变量和启动倒计时的方法隔开,所以我们需要一个变量来保存对应的比对时间(compareTime)
/**
* 待比较时间
*/
compareTime: Date;
@Input()
set startTime(value: Date) {
this._startTime = value;
this.processTime();
}
@Input()
set endTime(value: Date) {
this._endTime = value;
this.processTime();
}
private processTime() {
console.log(this._startTime);
const now = new Date();
//尚未开始
if (this._startTime > now) {
this.timerType = "开始";
this.compareTime = this._startTime;
}
//已开始尚未结束
else if (this._endTime > now) {
this.timerType = " 结束";
this.compareTime = this._endTime;
}
//已结束
else {
this.timerType = "已结束";
}
}
启动和停止倒计时
现在,我们已经做完了所有的准备工作,需要启动(start)或停止(stop)倒计时了,但这个启动或停止需要由父组件来控制,所以我们只需要暴露两个方法供父组件调用即可。在一定业务场景下,父组件可能需要随时监听子组件运行状态,这时就需要使用@Output来传递消息
/**
* 倒计时控制器
*/
timer: any = null;
/**
* 当倒计时运行状态改变时输出当前状态
*/
@Output()
onStatusChange: EventEmitter<CountDownStatus> = new EventEmitter();
/**
* 处理时间,获取距离开始或结束剩余的天时分秒
* @param compareTime
* @param currentTime
*/
processCountDownTime(compareTime: Date) {
const currentTime: Date = new Date();
if (compareTime) {
const leftTime = new Date(compareTime).getTime() - currentTime.getTime(); //计算剩余的毫秒数
let days = parseInt("" + leftTime / 1000 / 60 / 60 / 24);
let hours = parseInt("" + leftTime / 1000 / 60 / 60 % 24);
let minutes = parseInt("" + leftTime / 1000 / 60 % 60);
let seconds = parseInt("" + leftTime / 1000 % 60);
days = CountDownComponent.checkTime(days);
hours = CountDownComponent.checkTime(hours);
minutes = CountDownComponent.checkTime(minutes);
seconds = CountDownComponent.checkTime(seconds);
if (days <= 0 && hours <= 0 && minutes <= 0 && seconds <= 0) {
this.stop();
return;
}
this.timeDay = days;
this.timeHour = hours;
this.timeMinute = minutes;
this.timeSecond = seconds;
}
}
start() {
this.processTime();
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
this.timer = setInterval(() => {
this.processCountDownTime(this.compareTime);
}, 1000);
this.onStatusChange.emit(CountDownStatus.STARTED);
}
stop() {
clearInterval(this.timer);
this.timer = null;
if (this.compareTime < new Date()) {
this.timerType = "已结束";
this.onStatusChange.emit(CountDownStatus.STOPPED);
} else {
this.timerType = "已暂停";
this.onStatusChange.emit(CountDownStatus.PAUSED);
}
}
/**
* 将0-9的数字前面加上0,例1变为01
* @param i
*/
static checkTime(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
共CountDownStatus为一个枚举,存放倒计时所有可能的状态
export enum CountDownStatus {
STARTED,
STOPPED,
PAUSED
}
输出倒计时当前状态
在一定业务场景下,父组件可能需要随时监听子组件运行状态,这时就需要使用@Output()
@Output()
onStatusChange: EventEmitter<CountDownStatus> = new EventEmitter();
使用CountDownComponent
引入ComponentModules
在使用组件的对应module.ts文件(例如login.module.ts)中引入ComponentsModule(不是CountDownComponent)
使用CountDownComponent
在父组件页面HTML中使用count-down标签,由于子组件共有两个输入变量startTime和endTime以及一个输出函数onStatusChange(),故在父组件TS中也需要对应的两个变量及一个处理方法
TS:
startTime: Date = new Date();
endTime: Date = new Date(new Date().setTime(new Date().getTime() + 3 * 24 * 60 * 60 * 1000));
onStatusChange(status: CountDownStatus) {
switch (status) {
case CountDownStatus.STARTED:
console.log("倒计时已开始");
break;
case CountDownStatus.STOPPED:
console.log("倒计时已结束");
break;
case CountDownStatus.PAUSED:
console.log("倒计时已暂停");
break;
default:
console.log("状态未知");
break;
}
}
HTML
<count-down [startTime]="startTime" [endTime]="endTime" (onStatusChange)="onStatusChange($event)"></count-down>
启动或停止倒计时
为了完成在父组件控制子组件运行状态,所以需要使用@ViewChild来取得子组件引用以完成父子组件通信,@ViewChild()的括号中大多数时候只需要传递所需取得到的子组件的名字即可。
@ViewChild(CountDownComponent) countDown: CountDownComponent;
现在已经取得了对子组件的应用,需要编写启动或停止方法
/**
* 启动结束方式,直接控制子组件方法
*/
start() {
this.countDown.start();
}
stop() {
this.countDown.stop();
}
接下来通过在HTML中使用button绑定点击事件来调用开始和结束方法
<ion-buttons>
<button ion-button round (click)="start1()">开始</button>
<button ion-button round (click)="stop1()">结束</button>
</ion-buttons>
本文完整代码可访问如下链接获取:
https://github.com/chenwei116057/ChenWei/tree/master/ionic/CountDownComponent