ionic自定义倒计时组件

创建CountDownComponent

ionic g component count-down

运行完成后会生成如下目录


ionic自定义倒计时组件
image.png

编写CountDownComponent

首先分析一下倒计时组件需要显示哪些元素,每个元素都需要在TS文件中定于对应变量:

  1. 剩余天数(day)
  2. 剩余小时数(hour)
  3. 剩余分钟数(minute)
  4. 剩余秒数(second)
  5. 当前状态(未开始,已开始未结束,已结束;timerType)

编写简单页面

<p>
  开始时间:{{_startTime}}
</p>
<p>
  结束时间:{{_endTime}}
</p>
<p>
  <span *ngIf="showCountDown">还有{{timeDay}}天{{timeHour}}小时{{timeMinute}}分{{timeSecond}}秒</span>&nbsp;{{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)

ionic自定义倒计时组件
image.png

使用CountDownComponent

父组件页面HTML中使用count-down标签,由于子组件共有两个输入变量startTimeendTime以及一个输出函数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