小程序倒计时组件,单个&多个

365 阅读1分钟

10186.jpg

👌 Start

组件目录
components/
   count-down/
     xxx.wxml
     xxx.json
     xxx.js
     utils.js


1、 倒计时组件代码

wxml


<view class="count-down">
  <slot wx:if="{{ useSlot }}"/>
  <block wx:else>{{ formattedTime }}</block>
</view>

json

{
  "component": true,
  "usingComponents": {}
}

js


import { isSameSecond, parseFormat, parseTimeData } from './utils';
function simpleTick(fn) {
  return setTimeout(fn, 30);
}
const componentOptions = {
  properties: {
    countIndex: Number, // 如果是列表性质的,可能会用到是哪一个结束了,这里要接受一个下标值。 结束后会返回该下标值
    useSlot: Boolean, // 是否使用插槽
    millisecond: Boolean,  // 是否启用毫秒级渲染
    // 倒计时时长,单位毫秒
    time: {
      type: Number,
      observer: 'reset',
    },
    // 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒
    format: {
      type: String,
      value: 'HH:mm:ss',
    },
    // 是否自动开始倒计时
    autoStart: {
      type: Boolean,
      value: true,
    },
  },
  // 组件数据
  data: {
    timeData: parseTimeData(0),
    formattedTime: '0',
  },
  // 组件方法
  methods: {
    // 开始
    start() {
      if (this.counting) {
        return;
      }
      this.counting = true;
      this.endTime = Date.now() + this.remain;
      this.tick();
    },
    // 暂停
    pause() {
      this.counting = false;
      clearTimeout(this.tid);
    },
    // 重置
    reset() {
      this.pause();
      this.remain = this.data.time;
      this.setRemain(this.remain);
      if (this.data.autoStart) {
        this.start();
      }
    },
    tick() {
      if (this.data.millisecond) {
        this.microTick();
      }
      else {
        this.macroTick();
      }
    },
    microTick() {
      this.tid = simpleTick(() => {
        this.setRemain(this.getRemain());
        if (this.remain !== 0) {
          this.microTick();
        }
      });
    },
    macroTick() {
      this.tid = simpleTick(() => {
        const remain = this.getRemain();
        if (!isSameSecond(remain, this.remain) || remain === 0) {
          this.setRemain(remain);
        }
        if (this.remain !== 0) {
          this.macroTick();
        }
      });
    },
    getRemain() {
      return Math.max(this.endTime - Date.now(), 0);
    },
    setRemain(remain) {
      this.remain = remain;
      const timeData = parseTimeData(remain);
      if (this.data.useSlot) {
        this.triggerEvent('change', timeData);
      }
      this.setData({
        formattedTime: parseFormat(this.data.format, timeData),
      });
      if (remain === 0) {
        this.pause();
        this.triggerEvent('finish', {count_index: this.data.countIndex || 0}); // 如果是列表性质的倒计时,可能会需要用到具体到是哪一个结束了,所以这个地方再返回传过来的下标值
      }
    },
},
  // 组件生命周期
  lifetimes: {
    detached() {
      clearTimeout(this.tid);
      this.tid = null;
    },
  },
}

Component(componentOptions)

utils.js

function padZero(num, targetLength = 2) {
    let str = num + '';
    while (str.length < targetLength) {
        str = '0' + str;
    }
    return str;
}
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
export function parseTimeData(time) {
    const days = Math.floor(time / DAY);
    const hours = Math.floor((time % DAY) / HOUR);
    const minutes = Math.floor((time % HOUR) / MINUTE);
    const seconds = Math.floor((time % MINUTE) / SECOND);
    const milliseconds = Math.floor(time % SECOND);
    return {
        days,
        hours,
        minutes,
        seconds,
        milliseconds,
    };
}
export function parseFormat(format, timeData) {
    const { days } = timeData;
    let { hours, minutes, seconds, milliseconds } = timeData;
    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));
    }
    return format.replace('SSS', padZero(milliseconds, 3));
}
export function isSameSecond(time1, time2) {
    return Math.floor(time1 / 1000) === Math.floor(time2 / 1000);
}


2、 页面使用方法

json

"usingComponents": { 
  "count-down": "/components/count-down/index"
}

wxml

<view wx:for="{{list}}" wx:key="index">
    <count-down 
    time="{{ item.time }}" 
    format="剩余时间:DD 天 HH 时 mm 分 ss 秒"
    bind:finish="finished"
    countIndex="{{index}}"  <!-- 如果不需要多个倒计时可以不传该字段(不想要结束回调单独处理某一个的也可以不用) >
    />
</view>

js

// 倒计时结束的回调
finished(e) { 
    console.log(`第${e.detail.count_index}倒计时结束了!`)
},

👌 End

以上代码参考 Vant组件实现,具体细节使用方法 请去Vant官网查看