小程序实现一个 倒计时组件
需求背景
- 要做一个倒计时,可能是天级别,也可能是日级别,时级别,而且每个有效订单都要用,就做成组件了
效果图
需求分析
- 需要一个未来的时间戳,或者在服务度直接下发一个未来到现在时间差,我们只需要做倒计时
- 进入页面,看是否已经结束, 如果没结束就调用倒计时函数
- 每隔1000,做时间戳(毫秒) -1000。边做tick ,边做时间格式化。
- 每次调用前,先清除上一个定时器
- 组件销毁的时候,也要清除一下定时器
代码实现
- 设置初始值,也是1秒,这里单位时毫秒
js
const interval = 1000;
- 进入页面,初始化完成。开始判断是否结束
js
lifetimes: {
ready() {
this.startCountdown();
},
detached() {
clearTimeout(this.timer);
},
},
js
startCountdown() {
const lastTime = this.initTime(this.properties.expireTime); // 这一步,如果服务端返回了未来到现在的差值,则不需要自己计算时间差了
// 如果最终时间 < 1000ms 说明 已经过期了,就不用展示倒计时了.
if (lastTime > interval) {
// 格式化要展示的数据
this.defaultFormat(lastTime);
this.setData({
isCountOver: true, // 标识可以显示倒计时
lastTime, // set lastTime
});
// 调用倒计时函数,主要的逻辑就是每隔1000ms ,让lastTime - 1000
this.tick();
}
},
初始化时间: 如果服务度返回了时间差,这一步不用处理
js
//初始化时间
initTime(expireTime) {
let lastTime = Number(new Date(expireTime * 1000)) - new Date().getTime();
console.log('lastTime', lastTime);
return Math.max(lastTime, 0);
},
时间的格式化处理,这里都是固定代码,没什么含量
js
//默认处理时间格式
defaultFormat(time) {
const days = 60 * 60 * 1000 * 24;
const hours = 60 * 60 * 1000;
const minutes = 60 * 1000;
const d = Math.floor(time / days);
const h = Math.floor((time % days) / hours);
const m = Math.floor((time % hours) / minutes);
const s = Math.floor((time % minutes) / 1000);
this.setData({
d: this.fixedZero(d),
h: this.fixedZero(h),
m: this.fixedZero(m),
s: this.fixedZero(s),
});
},
// 格式化时间加0
fixedZero(val) {
return val < 10 ? `0${val}` : val;
},
tick 倒计时函数
js
tick() {
let { lastTime } = this.data;
this.timer = setTimeout(() => {
// 每次定时器之前,先把上一个定时器清除
clearTimeout(this.timer);
// 如果倒计时结束,这是结束的状态
if (lastTime < interval) {
this.setData({
lastTime: 0,
isCountOver: false,
});
} else {
// 如果倒计时正常,则每次 -1000 ,并且格式化时间。再次调用tick,直到倒计时结束
lastTime -= 1000;
this.setData(
{
lastTime,
},
() => {
this.defaultFormat(lastTime);
this.tick();
},
);
}
}, interval);
},
完整代码
- 父组件(我这里传了一个比较大的时间戳,2024,10.1结束的时间戳)
js
<order-time expireTime="{{ 1727712000 }}">
<view slot="desc">还剩</view>
</order-time>
- 子组件 (wxml)
js
<view wx:if="{{ isCountOver }}" class="timer-wrap">
<slot name="desc" />
<view class="reset-time">
<text wx:if="{{ d != '00' }}"> {{ d }}天</text>
{{ h }}:{{ m }}:{{ s }}</view
>
</view>
<view wx:else class="reset-time"> {{ emptyType === '1' ? '已超时': '' }} </view>
- 子组件 (js)
js
let interval = 1000;
Component({
options: {
multipleSlots: true,
},
properties: {
expireTime: {
type: String,
},
emptyType: {
type: String,
value: '1',
},
},
lifetimes: {
ready() {
this.startCountdown();
},
detached() {
clearTimeout(this.timer);
},
},
/**
* 组件的初始数据
*/
data: {
d: 0, //天
h: 0, //时
m: 0, //分
s: 0, //秒
lastTime: '', //倒计时的时间戳
isCountOver: false, // 倒计时是否完成
},
/**
* 组件的方法列表
*/
methods: {
startCountdown() {
const lastTime = this.initTime(this.properties.expireTime);
if (lastTime > interval) {
this.defaultFormat(lastTime);
this.setData({
isCountOver: true,
lastTime,
});
this.tick();
}
},
//默认处理时间格式
defaultFormat(time) {
const days = 60 * 60 * 1000 * 24;
const hours = 60 * 60 * 1000;
const minutes = 60 * 1000;
const d = Math.floor(time / days);
const h = Math.floor((time % days) / hours);
const m = Math.floor((time % hours) / minutes);
const s = Math.floor((time % minutes) / 1000);
this.setData({
d: this.fixedZero(d),
h: this.fixedZero(h),
m: this.fixedZero(m),
s: this.fixedZero(s),
});
},
//定时事件
tick() {
let { lastTime } = this.data;
this.timer = setTimeout(() => {
clearTimeout(this.timer);
if (lastTime < interval) {
this.setData({
lastTime: 0,
isCountOver: false,
});
} else {
lastTime -= 1000;
this.setData(
{
lastTime,
},
() => {
this.defaultFormat(lastTime);
this.tick();
},
);
}
}, interval);
},
//初始化时间
initTime(expireTime) {
let lastTime = Number(new Date(expireTime * 1000)) - new Date().getTime();
console.log('lastTime', lastTime);
return Math.max(lastTime, 0);
},
// 格式化时间加0
fixedZero(val) {
return val < 10 ? `0${val}` : val;
},
},
});
- 遇到相关变量,自己更改即可