@ciri/ngx-countdown
Version:
An angular countdown component.
461 lines (454 loc) • 12.4 kB
JavaScript
import { EventEmitter, Component, ChangeDetectionStrategy, ChangeDetectorRef, Input, Output, NgModule } from '@angular/core';
import { interval } from 'rxjs';
import { animationFrame } from 'rxjs/internal/scheduler/animationFrame';
import { CommonModule } from '@angular/common';
/**
* @fileoverview added by tsickle
* Generated from: lib/utils.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @param {?} num
* @param {?=} n
* @return {?}
*/
function padZero(num, n) {
if (n === void 0) { n = 2; }
/** @type {?} */
var str = num + '';
if (str.length >= n) {
return str;
}
return (Array(n).join('0') + str).slice(-n);
}
/** @type {?} */
var SECOND = 1000;
/** @type {?} */
var MINUTE = 60 * SECOND;
/** @type {?} */
var HOUR = 60 * MINUTE;
/** @type {?} */
var DAY = 24 * HOUR;
/**
* @param {?} time
* @return {?}
*/
function parseTimeData(time) {
/** @type {?} */
var days = Math.floor(time / DAY);
/** @type {?} */
var hours = Math.floor((time % DAY) / HOUR);
/** @type {?} */
var minutes = Math.floor((time % HOUR) / MINUTE);
/** @type {?} */
var seconds = Math.floor((time % MINUTE) / SECOND);
/** @type {?} */
var milliseconds = Math.floor(time % SECOND);
return {
days: days,
hours: hours,
minutes: minutes,
seconds: seconds,
milliseconds: milliseconds
};
}
/**
* @param {?} format
* @param {?} timeData
* @return {?}
*/
function parseFormat(format, timeData) {
var days = timeData.days, hours = timeData.hours, minutes = timeData.minutes, seconds = timeData.seconds, milliseconds = timeData.milliseconds;
if (format.indexOf('DD') === -1) {
hours += days * 24;
}
else {
format = format.replace('DD', padZero(days));
}
if (format.indexOf('HH') === -1) {
minutes += hours * 60;
}
else {
format = format.replace('HH', padZero(hours));
}
if (format.indexOf('mm') === -1) {
seconds += minutes * 60;
}
else {
format = format.replace('mm', padZero(minutes));
}
if (format.indexOf('ss') === -1) {
milliseconds += seconds * 1000;
}
else {
format = format.replace('ss', padZero(seconds));
}
if (format.indexOf('S') !== -1) {
/** @type {?} */
var ms = padZero(milliseconds, 3);
if (format.indexOf('SSS') !== -1) {
format = format.replace('SSS', ms);
}
else if (format.indexOf('SS') !== -1) {
format = format.replace('SS', ms.slice(0, 2));
}
else {
format = format.replace('S', ms.charAt(0));
}
}
return format;
}
/**
* @param {?} time1
* @param {?} time2
* @return {?}
*/
function isSameSecond(time1, time2) {
return Math.floor(time1 / 1000) === Math.floor(time2 / 1000);
}
/**
* @fileoverview added by tsickle
* Generated from: lib/countdown.component.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @record
*/
function CountdownData() { }
if (false) {
/** @type {?} */
CountdownData.prototype.remain;
/** @type {?} */
CountdownData.prototype.formattedTime;
/** @type {?} */
CountdownData.prototype.fragments;
}
/** @enum {number} */
var CountdownState = {
/** 暂停状态 */
paused: 0,
/** 运行状态 */
playing: 1,
/** 完成状态 */
finished: 2,
};
CountdownState[CountdownState.paused] = 'paused';
CountdownState[CountdownState.playing] = 'playing';
CountdownState[CountdownState.finished] = 'finished';
/**
* 所有组件实例使用同一个 interval 流,以免实例多了后造成卡顿
* @type {?}
*/
var instances = [];
/** @type {?} */
var counter$ = interval(0, animationFrame);
/** @type {?} */
var counterSub;
/**
* @return {?}
*/
function setupCounter() {
destroyCounter();
counterSub = counter$.subscribe((/**
* @return {?}
*/
function () {
for (var i = 0; i < instances.length; i++) {
/** @type {?} */
var inst = instances[i];
if (inst.state !== CountdownState.playing) {
continue;
}
if (inst.remain <= 0) {
inst.state = CountdownState.finished;
inst.cdr.detectChanges();
inst.finish.emit();
continue;
}
/** @type {?} */
var remain = Math.max(inst.endTime - Date.now(), 0);
if (!inst.millisecond) {
if (!isSameSecond(remain, inst.remain) || remain === 0) {
inst.remain = remain;
inst.tick.emit(inst.remain);
}
}
else {
inst.remain = remain;
inst.tick.emit(inst.remain);
}
inst.cdr.detectChanges();
}
}));
}
/**
* @return {?}
*/
function destroyCounter() {
counterSub && counterSub.unsubscribe();
}
/**
* 倒计时组件
*/
var CountdownComponent = /** @class */ (function () {
function CountdownComponent(cdr) {
this.cdr = cdr;
/**
* 格式
*/
this.format = 'HH:mm:ss';
/**
* 是否自动开始
*/
this.autoStart = true;
/**
* 是否开启毫秒级渲染
*/
this.millisecond = false;
/**
* 倒计时完毕时触发
*/
this.finish = new EventEmitter();
/**
* 每倒计时一次都触发
*/
this.tick = new EventEmitter();
this.state = CountdownState.paused;
this._time = 60000;
}
Object.defineProperty(CountdownComponent.prototype, "time", {
get: /**
* @return {?}
*/
function () {
return this._time;
},
/** 总毫秒数 */
set: /**
* 总毫秒数
* @param {?} value
* @return {?}
*/
function (value) {
this._time = Math.max(value, 0);
this.reset();
},
enumerable: true,
configurable: true
});
Object.defineProperty(CountdownComponent.prototype, "data", {
get: /**
* @return {?}
*/
function () {
/** @type {?} */
var noMillisecond = this.format.indexOf('S') === -1;
/** @type {?} */
var isPlaying = this.state === CountdownState.playing
// 即使 format 中没有设置毫秒,程序计算时候也会将毫秒计算进去,导致运行时秒数看上去总是小 1
// 此处在渲染时候手动 +1 秒,以追求视觉统一
;
// 即使 format 中没有设置毫秒,程序计算时候也会将毫秒计算进去,导致运行时秒数看上去总是小 1
// 此处在渲染时候手动 +1 秒,以追求视觉统一
/** @type {?} */
var timeData = parseTimeData(this.remain + (noMillisecond && isPlaying ? 1000 : 0));
/** @type {?} */
var formattedTime = parseFormat(this.format, timeData);
return {
formattedTime: formattedTime,
remain: this.remain,
fragments: formattedTime.split(':')
};
},
enumerable: true,
configurable: true
});
/**
* @return {?}
*/
CountdownComponent.prototype.ngOnInit = /**
* @return {?}
*/
function () {
instances.push(this);
if (instances.length === 1) {
setupCounter();
}
};
/**
* @return {?}
*/
CountdownComponent.prototype.ngOnDestroy = /**
* @return {?}
*/
function () {
this.pause();
/** @type {?} */
var index = instances.indexOf(this);
if (index !== -1) {
instances.splice(index, 1);
}
if (instances.length === 0) {
destroyCounter();
}
};
/**
* 继续倒计时
*/
/**
* 继续倒计时
* @return {?}
*/
CountdownComponent.prototype.start = /**
* 继续倒计时
* @return {?}
*/
function () {
if (this.state === CountdownState.playing) {
return;
}
if (this.state === CountdownState.finished) {
this.remain = this.time;
}
this.endTime = Date.now() + this.remain;
this.state = CountdownState.playing;
};
/**
* 暂停倒计时
*/
/**
* 暂停倒计时
* @return {?}
*/
CountdownComponent.prototype.pause = /**
* 暂停倒计时
* @return {?}
*/
function () {
this.state = CountdownState.paused;
};
/**
* 重置倒计时
*/
/**
* 重置倒计时
* @return {?}
*/
CountdownComponent.prototype.reset = /**
* 重置倒计时
* @return {?}
*/
function () {
this.pause();
this.remain = this.time;
this.cdr.detectChanges();
if (this.autoStart) {
this.start();
}
};
CountdownComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-countdown',
template: "<ng-container *ngIf=\"!render\">{{ data.formattedTime }}</ng-container>\n<ng-container *ngTemplateOutlet=\"render; context: { $implicit: data }\"></ng-container>\n",
changeDetection: ChangeDetectionStrategy.OnPush
}] }
];
/** @nocollapse */
CountdownComponent.ctorParameters = function () { return [
{ type: ChangeDetectorRef }
]; };
CountdownComponent.propDecorators = {
format: [{ type: Input }],
autoStart: [{ type: Input }],
millisecond: [{ type: Input }],
render: [{ type: Input }],
time: [{ type: Input }],
finish: [{ type: Output }],
tick: [{ type: Output }]
};
return CountdownComponent;
}());
if (false) {
/**
* 格式
* @type {?}
*/
CountdownComponent.prototype.format;
/**
* 是否自动开始
* @type {?}
*/
CountdownComponent.prototype.autoStart;
/**
* 是否开启毫秒级渲染
* @type {?}
*/
CountdownComponent.prototype.millisecond;
/**
* 自定义模版
* @type {?}
*/
CountdownComponent.prototype.render;
/**
* 倒计时完毕时触发
* @type {?}
*/
CountdownComponent.prototype.finish;
/**
* 每倒计时一次都触发
* @type {?}
*/
CountdownComponent.prototype.tick;
/** @type {?} */
CountdownComponent.prototype.state;
/**
* @type {?}
* @private
*/
CountdownComponent.prototype._time;
/**
* 剩余毫秒数
* @type {?}
* @private
*/
CountdownComponent.prototype.remain;
/**
* @type {?}
* @private
*/
CountdownComponent.prototype.endTime;
/**
* @type {?}
* @private
*/
CountdownComponent.prototype.cdr;
}
/**
* @fileoverview added by tsickle
* Generated from: lib/countdown.module.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
var CountdownModule = /** @class */ (function () {
function CountdownModule() {
}
CountdownModule.decorators = [
{ type: NgModule, args: [{
declarations: [CountdownComponent],
imports: [CommonModule],
exports: [CountdownComponent]
},] }
];
return CountdownModule;
}());
/**
* @fileoverview added by tsickle
* Generated from: public-api.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* Generated from: ciri-ngx-countdown.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
export { CountdownComponent, CountdownModule, CountdownState };
//# sourceMappingURL=ciri-ngx-countdown.js.map