Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2020-03-20 实现一个倒计时工具 #14

Open
fanmingfei opened this issue Mar 19, 2020 · 5 comments
Open

2020-03-20 实现一个倒计时工具 #14

fanmingfei opened this issue Mar 19, 2020 · 5 comments

Comments

@fanmingfei
Copy link
Member

fanmingfei commented Mar 19, 2020

设计一个倒计时工具,考虑稳定性、复用性、可用性。
相当于自己沉淀一个倒计时组件,未来工作中可以直接拿过来用。
做到简单易懂易用,未来进入公司工作到时候,遇到倒计时需求的时候,可以自信到把现在写的代码拿出来用,或许那时候已经是一个好用的npm包了。

@fanmingfei fanmingfei changed the title 2020-03-19 禁止转化 2020-03-19 待定 Mar 19, 2020
@fanmingfei fanmingfei changed the title 2020-03-19 待定 2020-03-19 实现一个倒计时插件 Mar 20, 2020
@fanmingfei fanmingfei changed the title 2020-03-19 实现一个倒计时插件 2020-03-19 实现一个倒计时工具 Mar 20, 2020
@joker1148
Copy link

<cacuTime :endTime="time"/>

<template>
  <div>
    <div>
       剩余时间:{{diffTime}}
    </div>
    <div>
       截止时间:{{time}}
    </div> 
  </div>
</template>

<script>
  export default {
    props: ["endTime"],
    data() {
      return {
        time: this.endTime,
        diffTime: ""
      }
    },
    created() {
      // console.log(this.time)
      let a = this.caculate()
      console.log(a)
    },
    methods: {
      filter(num){
        return num.length < 2 ? ("0"+num) : num
      },
      caculate() {
        let now = new Date()
        let end = new Date(this.time)
        let diff = end - now
        console.log(diff)
        if(diff < 0){
          alert("时间到")
          this.diffTime = "0天00:00:00"
          return
        }
        let day = Math.floor(diff / 86400000)
        let hour = Math.floor(diff / (1000 * 60 * 60) % 24).toString()
        let min = Math.floor(diff / (1000 * 60) % 60).toString()
        let sec = Math.floor(diff / 1000 % 60).toString()
        let res = day + "天" + this.filter(hour) + ":" + this.filter(min) + ":" + this.filter(sec)
        this.diffTime = res
        setTimeout(this.caculate,1000)
      }
    }
  }
</script>

<style>
</style>

@xiaosen7
Copy link

xiaosen7 commented Mar 22, 2020

CountDown

import React, { useEffect, useMemo, useRef } from 'react';
import { CountDownProps, CountDownComponent } from './types';
import { useTime, defaultTransform } from './utils';

let timer: any;

const CountDown: CountDownComponent<CountDownProps> = ({
    endTime = 0,//结束日期:日期对象或者是时间戳
    hours = 0,//倒计时多少小时
    minutes = 0,//倒计时多少分钟
    seconds = 0,//倒计时多少秒
    onTimeout,//时间到的事件处理
    style,//样式
    transform = defaultTransform,//返回的react元素将被渲染
    onChange,//一秒触发一次
}: CountDownProps) => {

    const { current: timeMap } = useRef({ endTime, hours, minutes, seconds });

    const remainingSeconds: number = useMemo(() => {
        let { endTime, hours, minutes, seconds } = timeMap;
        if (!endTime) {
            return hours * 60 * 60 + minutes * 60 + seconds;
        } else {
            endTime = typeof endTime === "number" ? new Date(endTime) : endTime;
            return Math.round((endTime.getTime() - Date.now()) / 1000);
        }
    }, [timeMap]);

    const diffObj = useRef(remainingSeconds);

    const [timeObj, setTime] = useTime(remainingSeconds);

    useEffect(() => {

        let remainingSeconds = diffObj.current;

        const timerHandler = () => {
            if (remainingSeconds === 0) {
                clearInterval(timer);
                return onTimeout();
            }

            remainingSeconds -= 1;

            if (typeof onChange === 'function') {
                const res = onChange(remainingSeconds);
                switch (typeof res) {
                    case 'number':
                        remainingSeconds = res;
                        break;
                    case 'boolean':
                        if (!res) {
                            return clearInterval(timer);
                        }
                }
            }

            setTime(remainingSeconds);
        }
        timer = setInterval(timerHandler, 1000);

        CountDown.continue = () => {
            timer = setInterval(timerHandler, 1000);
        }

        return () => clearInterval(timer);

    }, [onTimeout, setTime, onChange]);

    const jsx = useMemo(() => transform(timeObj, diffObj.current), [transform, timeObj, diffObj]);

    return (
        <span style={style}>{jsx}</span>
    )
}

CountDown.pause = () => {
    clearInterval(timer);
}

export default CountDown;

utils

import { Transform, UseTime } from './types';
import { useState } from 'react';

export const patchZero = (s: string) => s.length < 2 ? '0' + s : s;

export const defaultTransform: Transform = ({ hours, minutes, seconds }, diffSeconds: number) => {
    return `${patchZero(String(hours))}:${patchZero(String(minutes))}:${patchZero(String(seconds))}`;
}

export const useTime: UseTime = (diffSeconds) => {

    const [time, setTime] = useState(diffSeconds);

    let current = time;
    const seconds = current % 60;
    current -= seconds;

    const minutes = current / 60 % 24;
    current -= minutes * 60;

    const hours = Math.trunc(current / 60 / 60);

    return [
        { hours, minutes, seconds },
        setTime
    ]
}

types

import { FC } from 'react';
export interface TimeObj {
    hours: number,
    minutes: number,
    seconds: number
}
export type CountDownComponent<T> = {
    pause: () => void
    continue?: () => void
} & FC<T>
export interface CountDownProps {
    endTime?: Date | number,
    onTimeout: Function
    style?: React.CSSProperties,
    transform?: Transform,
    hours?: number,
    minutes?: number,
    seconds?: number,
    onChange?: (remainingSeconds: number) => number | boolean | void
}
export type Transform = (timeObj: TimeObj, diffSeconds: number) => React.ReactNode;
export type UseTime = (diffSeconds: number) => [
    TimeObj,
    Function
];

@LiZhaji
Copy link

LiZhaji commented Mar 23, 2020

class Timer {
  constructor(props) {
    const {
      container, // 倒计时容器,dom元素
      time, // 倒计时间,秒
      onStart = () => {}, // 点击开始回调,可选
      onStop = () => {}, // 点击暂停回调,可选
      onChange = () => {}, // 每一秒回调,可选
      onFinish = () => {}, // 倒计时结束回调,可选
      onReset = () => {} // 点击重置回调,可选
    } = props;

    this.container = container;
    this.curTime = this.totalTime = time;
    this.gapTime = 1000;

    this.onStart = onStart;
    this.onStop = onStop;
    this.onChange = onChange;
    this.onFinish = onFinish;
    this.onReset = onReset;

    this.stopFlag = false;
    this.timeBox = document.createElement("div");
  }
  init() {
    this.timeBox.innerText = this.formatTime();
    this.container.append(this.timeBox);
    this.printBtns();
  }
  printBtns() {
    const startBtn = document.createElement("button");
    startBtn.setAttribute("class", "operator-btn");
    startBtn.innerText = "开始";
    startBtn.addEventListener("click", e => {
      this.container.replaceChild(stopBtn, e.target);
      this.start();
      this.onStart();
    });

    const stopBtn = document.createElement("button");
    stopBtn.setAttribute("class", "operator-btn");
    stopBtn.innerText = "暂停";
    stopBtn.addEventListener("click", e => {
      this.container.replaceChild(startBtn, e.target);
      this.stop();
      this.onStop();
    });

    const resetBtn = document.createElement("button");
    resetBtn.innerText = "重置";
    resetBtn.addEventListener("click", e => {
      if (!this.stopFlag) {
        this.stop();
        this.container.replaceChild(
          startBtn,
          this.container.querySelector(".operator-btn")
        );
      }
      this.resetFlag = true;
      this.curTime = this.totalTime + 1;
      this.printTime();
      this.onReset();
    });

    this.container.append(startBtn);
    this.container.append(resetBtn);
  }
  start() {
    this.stopFlag = false;
    let raf;
    let startTime = Date.now();
    let endTime;
    const second = () => {
      raf = requestAnimationFrame(second);
      if (this.stopFlag) {
        cancelAnimationFrame(raf);
        // 继续计时
        if (!this.resetFlag) {
          this.gapTime -= Date.now() - startTime;
        }
        return;
      }
      endTime = Date.now();
      // 每隔1秒显示
      if (endTime - startTime < this.gapTime) {
        return;
      }

      if (this.curTime <= 1) {
        cancelAnimationFrame(raf);
        this.finish();
        return;
      }
      this.printTime();
      this.onChange();
      startTime = Date.now();
    };
    raf = requestAnimationFrame(second);
  }
  stop() {
    this.stopFlag = true;
  }
  finish() {
    this.timeBox.innerText = "时间到!";
    this.container.replaceChild(
      this.timeBox,
      this.container.querySelector("div")
    );
    this.onFinish();
  }
  printTime() {
    this.timeBox.innerText = this.formatTime(--this.curTime);
    this.container.replaceChild(
      this.timeBox,
      this.container.querySelector("div")
    );
    this.gapTime = 1000;
  }
  formatTime(seconds = this.curTime) {
    const sec = seconds % 60;
    const min = Math.trunc(seconds / 60) % 60;
    const hour = Math.trunc(seconds / 60 / 60) % 60;
    const day = Math.trunc(seconds / 60 / 60 / 24) % 60;
    return (
      (day ? day + "天" : "") +
      (hour ? hour + "小时" : "") +
      (min ? min + "分钟" : "") +
      (sec + "秒")
    );
  }
}

function countdown(props) {
  const box = new Timer(props);
  box.init();
}

export default countdown;

@fanmingfei
Copy link
Member Author

其实不需要有UI部分,只需要提供API出来,不管是VUE React 还是不需要用到UI的程序都可以来用。

@fanmingfei fanmingfei changed the title 2020-03-19 实现一个倒计时工具 2020-03-20 实现一个倒计时工具 Mar 24, 2020
@LiZhaji
Copy link

LiZhaji commented Mar 24, 2020

向外暴露4个方法

  • init 初始化时间、指定回调
  • start 开始倒计时
  • stop 停止倒计时
  • reset 重置倒计时
class Timer {
  gapTime = 1000;
  stopFlag = false;
  resetFlag = false;

  init(time, onChange, onFinish) {
    this.curTime = this.totalTime = time;
    this.onChange = onChange;
    this.onFinish = onFinish;
  }
  start() {
    this.stopFlag = false;
    this.resetFlag = false;
    let raf;
    let startTime = Date.now();
    let endTime;
    const second = () => {
      raf = requestAnimationFrame(second);
      if (this.stopFlag) {
        cancelAnimationFrame(raf);
        // 继续计时
        if (!this.resetFlag) {
          this.gapTime -= Date.now() - startTime;
        }
        return;
      }
      endTime = Date.now();
      // 每隔1秒显示
      if (endTime - startTime < this.gapTime) {
        return;
      }

      if (this.curTime <= 1) {
        cancelAnimationFrame(raf);
        this.onFinish();
        return;
      }
      this.onChange(--this.curTime);
      this.gapTime = 1000
      startTime = Date.now();
    };
    raf = requestAnimationFrame(second);
  }
  stop() {
    this.stopFlag = true;
  }
  reset() {
    if (!this.stopFlag) {
      this.stop();
    }
    this.resetFlag = true;
    this.curTime = this.totalTime;
  }
}

export default new Timer();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants