倒计时之翻牌篇

话不多说看图

在这里插入图片描述
使用组件

// 这里面的参数就是你要倒计时结束的时间
<TheCountdown :deadline="'2020-10-05 23:59:59'"></TheCountdown>

组件 直接拿走 调用就行

<template>
  <div class=" flip-clock" style="min-width:30rem;">
    <template v-for="data in timeData" v-show="show">
      <span
        v-bind:key="data.label"
        class="flip-clock__piece"
        :id="data.elementId"
        v-show="data.show"
      >
        <span class="flip-clock__card flip-card">
          <b class="flip-card__top">{{ data.current | twoDigits }}</b>
          <b
            class="flip-card__bottom"
            v-bind:data-value="data.current | twoDigits"
          ></b>
          <b
            class="flip-card__back"
            v-bind:data-value="data.previous | twoDigits"
          ></b>
          <b
            class="flip-card__back-bottom"
            v-bind:data-value="data.previous | twoDigits"
          ></b>
        </span>
      </span>
    </template>
  </div>
</template>

<script>
let interval = null;
const uuidv4 = require("uuid/v4");
export default {
  name: "flipCountdown",
  props: {
    deadline: {
      type: String
    },
    stop: {
      type: Boolean
    },
    showDays: {
      type: Boolean,
      required: false,
      default: true
    },
    labels: {
      type: Object,
      required: false,
      default: function() {
        return {
          days: "天",
          hours: "时",
          minutes: "分",
          seconds: "秒"
        };
      }
    }
  },
  data() {
    const uuid = uuidv4();
    return {
      now: "",
      date: null,
      interval: null,
      diff: 0,
      show: false,
      timeData: [
        {
          current: 0,
          previous: 0,
          label: this.labels.days,
          elementId: "flip-card-days-" + uuid,
          show: this.showDays
        },
        {
          current: 0,
          previous: 0,
          label: this.labels.hours,
          elementId: "flip-card-hours-" + uuid,
          show: true
        },
        {
          current: 0,
          previous: 0,
          label: this.labels.minutes,
          elementId: "flip-card-minutes-" + uuid,
          show: true
        },
        {
          current: 0,
          previous: 0,
          label: this.labels.seconds,
          elementId: "flip-card-seconds-" + uuid,
          show: true
        }
      ]
    };
  },
  created() {
    if (!this.deadline) {
      throw new Error("Missing props 'deadline'");
    }
    const endTime = this.deadline;
    this.date = Math.trunc(Date.parse(endTime.replace(/-/g, "/")) / 1000);
    // eslint-disable-next-line no-console
    if (!this.date) {
      throw new Error("Invalid props value, correct the 'deadline'");
    }
    this.now = Math.trunc(new Date().getTime() / 1000);
    this.interval = setInterval(() => {
      this.now = Math.trunc(new Date().getTime() / 1000);
    }, 1000);
  },
  mounted() {
    if (this.diff !== 0) {
      this.show = true;
    }
  },
  computed: {
    seconds() {
      return Math.trunc(this.diff) % 60;
    },
    minutes() {
      return Math.trunc(this.diff / 60) % 60;
    },
    hours() {
      return Math.trunc(this.diff / 60 / 60) % 24;
    },
    days() {
      return Math.trunc(this.diff / 60 / 60 / 24);
    }
  },
  watch: {
    // eslint-disable-next-line no-unused-vars
    deadline: function(newVal, oldVal) {
      const endTime = this.deadline;
      this.date = Math.trunc(Date.parse(endTime.replace(/-/g, "/")) / 1000);
      if (!this.date) {
        throw new Error("Invalid props value, correct the 'deadline'");
      }
    },
    // eslint-disable-next-line no-unused-vars
    now(value) {
      this.diff = this.date - this.now;
      if (this.diff <= 0 || this.stop) {
        this.diff = 0;
        this.updateTime(3, 0);
      } else {
        this.updateAllCards();
      }
    },
    diff(value) {
      if (value === 0) {
        this.$emit("timeElapsed");
        this.updateAllCards();
      }
    }
  },
  filters: {
    twoDigits(value) {
      if (value.toString().length <= 1) {
        return "0" + value.toString();
      }
      return value.toString();
    }
  },
  methods: {
    updateAllCards() {
      this.updateTime(0, this.days);
      this.updateTime(1, this.hours);
      this.updateTime(2, this.minutes);
      this.updateTime(3, this.seconds);
    },
    updateTime(idx, newValue) {
      if (idx >= this.timeData.length || newValue === undefined) {
        return;
      }
      if (window["requestAnimationFrame"]) {
        this.frame = requestAnimationFrame(this.updateTime.bind(this));
      }
      const d = this.timeData[idx];
      const val = newValue < 0 ? 0 : newValue;
      const el = document.querySelector(`#${d.elementId}`);
      if (val !== d.current) {
        d.previous = d.current;
        d.current = val;
        if (el) {
          el.classList.remove("flip");
          void el.offsetWidth;
          el.classList.add("flip");
        }
        if (idx === 0) {
          const els = el.querySelectorAll("span b");
          if (els) {
            for (let e of els) {
              const cls = e.classList[0];
              if (newValue / 1000 >= 1) {
                if (!cls.includes("-4digits")) {
                  const newCls = cls + "-4digits";
                  e.classList.add(newCls);
                  e.classList.remove(cls);
                }
              } else {
                if (cls.includes("-4digits")) {
                  const newCls = cls.replace("-4digits", "");
                  e.classList.add(newCls);
                  e.classList.remove(cls);
                }
              }
            }
          }
        }
      }
    }
  },
  beforeDestroy() {
    if (window["cancelAnimationFrame"]) {
      cancelAnimationFrame(this.frame);
    }
  },
  destroyed() {
    clearInterval(interval);
  }
};
</script>

<style scoped lang="less">
.flip-clock {
  text-align: center;
  perspective: 37.5rem;
  margin: 0 auto;
  *,
  *:before,
  *:after {
    box-sizing: border-box;
  }
}
.flip-clock__piece {
  display: inline-block;
  margin: 0 0.2vw;
  @media (min-width: 1000px) {
    margin: 0 0.3125rem;
  }
}
.flip-clock__slot {
  font-size: 1rem;
  line-height: 1.5;
  display: block;
}
@halfHeight: 0.72em;
@borderRadius: 0.15em;
.flip-card {
  display: block;
  position: relative;
  padding-bottom: @halfHeight;
  font-size: 2.25rem;
  line-height: 0.95;
}
@media (min-width: 1000px) {
  .flip-clock__slot {
    font-size: 1.2rem;
  }
  .flip-card {
    font-size: 3rem;
  }
}
.flip-card__top,
.flip-card__bottom,
.flip-card__back-bottom,
.flip-card__back::before,
.flip-card__back::after {
  display: block;
  height: @halfHeight;
  color: #fff;
  background: #222;
  padding: 0.23em 0.15em 0.4em;
  border-radius: @borderRadius @borderRadius 0 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transform-style: preserve-3d;
  width: 2.1em;
  height: @halfHeight;
}
.flip-card__top-4digits,
.flip-card__bottom-4digits,
.flip-card__back-bottom-4digits,
.flip-card__back-4digits::before,
.flip-card__back-4digits::after {
  display: block;
  height: @halfHeight;
  color: #fff;
  background: #222;
  padding: 0.23em 0.15em 0.4em;
  border-radius: @borderRadius @borderRadius 0 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transform-style: preserve-3d;
  width: 2.65em;
  height: @halfHeight;
}
.flip-card__bottom,
.flip-card__back-bottom,
.flip-card__bottom-4digits,
.flip-card__back-bottom-4digits {
  color: #fff;
  position: absolute;
  top: 50%;
  left: 0;
  border-top: solid 0.0625rem #000;
  background: #333;
  border-radius: 0 0 @borderRadius @borderRadius;
  pointer-events: none;
  overflow: hidden;
  z-index: 20;
}
.flip-card__back-bottom,
.flip-card__back-bottom-4digits {
  z-index: 10;
}
.flip-card__bottom::after,
.flip-card__back-bottom::after,
.flip-card__bottom-4digits::after,
.flip-card__back-bottom-4digits::after {
  display: block;
  margin-top: -@halfHeight;
}
.flip-card__back::before,
.flip-card__bottom::after,
.flip-card__back-bottom::after,
.flip-card__back-4digits::before,
.flip-card__bottom-4digits::after,
.flip-card__back-bottom-4digits::after {
  content: attr(data-value);
}
.flip-card__back,
.flip-card__back-4digits {
  position: absolute;
  top: 0;
  height: 100%;
  left: 0%;
  pointer-events: none;
}
.flip-card__back::before,
.flip-card__back-4digits::before {
  position: relative;
  overflow: hidden;
  z-index: -10;
}
.flip .flip-card__back::before,
.flip .flip-card__back-4digits::before {
  z-index: 10;
  animation: flipTop 0.3s cubic-bezier(0.37, 0.01, 0.94, 0.35);
  animation-fill-mode: both;
  transform-origin: center bottom;
}
.flip .flip-card__bottom,
.flip .flip-card__bottom-4digits {
  transform-origin: center top;
  animation-fill-mode: both;
  animation: flipBottom 0.6s cubic-bezier(0.15, 0.45, 0.28, 1);
}
@keyframes flipTop {
  0% {
    transform: rotateX(0deg);
    z-index: 20;
  }
  0%,
  99% {
    opacity: 1;
  }
  100% {
    transform: rotateX(-90deg);
    opacity: 0;
  }
}
@keyframes flipBottom {
  0%,
  50% {
    z-index: -10;
    transform: rotateX(90deg);
    opacity: 0;
  }
  51% {
    opacity: 1;
  }
  100% {
    opacity: 1;
    transform: rotateX(0deg);
    z-index: 50;
  }
}
</style>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值