时钟翻牌器(支持JS/Vue/React)

为了方便简洁做一个备份记录,详细的可以去我转载的那位写的他功能写的很细很全面

https://mp.weixin.qq.com/s/sA9S-mYAqVd527uggGeLfA

代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>


<div class="clock"id="clock">
  <div class="flip down">
    <div class="digital front number0"></div>
    <div class="digital back number1"></div>
  </div>
  <div class="flip down">
    <div class="digital front number0"></div>
    <div class="digital back number1"></div>
  </div>
  <em>:</em>
  <div class="flip down">
    <div class="digital front number0"></div>
    <div class="digital back number1"></div>
  </div>
  <div class="flip down">
    <div class="digital front number0"></div>
    <div class="digital back number1"></div>
  </div>
  <em>:</em>
  <div class="flip down">
    <div class="digital front number0"></div>
    <div class="digital back number1"></div>
  </div>
  <div class="flip down">
    <div class="digital front number0"></div>
    <div class="digital back number1"></div>
  </div>
</div>




</body>

</html>

<script>


  function Flipper(config) {
    // 默认配置
    this.config = {
      // 时钟模块的节点
      node: null,
      // 初始前牌文字
      frontText: 'number0',
      // 初始后牌文字
      backText: 'number1',
      // 翻转动画时间(毫秒,与翻转动画CSS 设置的animation-duration时间要一致)
      duration: 500
    }
    // 节点的原本class,与html对应,方便后面添加/删除新的class
    this.nodeClass = {
      flip: 'flip',
      front: 'digital front',
      back: 'digital back'
    }
    // 覆盖默认配置
    Object.assign(this.config, config)
    // 定位前后两个牌的DOM节点
    this.frontNode = this.config.node.querySelector('.front')
    this.backNode = this.config.node.querySelector('.back')
    // 是否处于翻牌动画过程中(防止动画未完成就进入下一次翻牌)
    this.isFlipping = false
    // 初始化
    this._init()
  }
  Flipper.prototype = {
    constructor: Flipper,
    // 初始化
    _init: function() {
      // 设置初始牌面字符
      this._setFront(this.config.frontText)
      this._setBack(this.config.backText)
    },
    // 设置前牌文字
    _setFront: function(className) {
      this.frontNode.setAttribute('class', this.nodeClass.front + ' '+ className)
    },
// 设置后牌文字
    _setBack: function(className) {
      this.backNode.setAttribute('class', this.nodeClass.back + ' '+ className)
    },
    _flip: function(type, front, back) {
      // 如果处于翻转中,则不执行
      if(this.isFlipping) {
        return false
      }
// 设置翻转状态为true
      this.isFlipping = true
      // 设置前牌文字
      this._setFront(front)
      // 设置后牌文字
      this._setBack(back)
      // 根据传递过来的type设置翻转方向
      let flipClass = this.nodeClass.flip;
      if(type === 'down') {
        flipClass += ' down'
      } else{
        flipClass += ' up'
      }
      // 添加翻转方向和执行动画的class,执行翻转动画
      this.config.node.setAttribute('class', flipClass + ' go')
      // 根据设置的动画时间,在动画结束后,还原class并更新前牌文字
      setTimeout(() => {
        // 还原class
        this.config.node.setAttribute('class', flipClass)
        // 设置翻转状态为false
        this.isFlipping = false
        // 将前牌文字设置为当前新的数字,后牌因为被前牌挡住了,就不用设置了。
        this._setFront(back)
      }, this.config.duration)
    },
// 下翻牌
    flipDown: function(front, back) {
      this._flip('down', front, back)
    },
// 上翻牌
    flipUp: function(front, back) {
      this._flip('up', front, back)
    }
  }

  // 定位时钟模块
  let clock = document.getElementById('clock')
  // 定位6个翻板
  let flips = clock.querySelectorAll('.flip')
  // 获取当前时间
  let now = new Date()
  // 格式化当前时间,例如现在是20:30:10,则输出"203010"字符串
  let nowTimeStr = formatDate(now, 'hhiiss')
  // 格式化下一秒的时间
  let nextTimeStr = formatDate(new Date(now.getTime() + 1000), 'hhiiss')
  // 定义牌板数组,用来存储6个Flipper翻板对象
  let flipObjs = []
  for(let i = 0; i < flips.length; i++) {
    // 创建6个Flipper实例,初始化并存入flipObjs
    flipObjs.push(new Flipper({
      // 每个Flipper实例按数组顺序与翻板DOM的顺序一一对应
      node: flips[i],
      // 按数组顺序取时间字符串对应位置的数字
      frontText: 'number'+ nowTimeStr[i],
      backText: 'number'+ nextTimeStr[i]
    }))
  }

  // 正则格式化日期
  function formatDate(date, dateFormat) {
    /* 单独格式化年份,根据y的字符数量输出年份
     * 例如:yyyy => 2019
     *      yy => 19
     *      y => 9
     */
    if(/(y+)/.test(dateFormat)) {
      dateFormat = dateFormat.replace(RegExp.$1, (date.getFullYear() + '').substr(4- RegExp.$1.length));
    }
    // 格式化月、日、时、分、秒
    let o = {
      'm+': date.getMonth() + 1,
      'd+': date.getDate(),
      'h+': date.getHours(),
      'i+': date.getMinutes(),
      's+': date.getSeconds()
    };
    for(let k in o) {
      if(new RegExp(`(${k})`).test(dateFormat)) {
        // 取出对应的值
        let str = o[k] + '';
        /* 根据设置的格式,输出对应的字符
             * 例如: 早上8时,hh => 08,h => 8
             * 但是,当数字>=10时,无论格式为一位还是多位,不做截取,这是与年份格式化不一致的地方
             * 例如: 下午15时,hh => 15, h => 15
             */
        dateFormat = dateFormat.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
      }
    }
    return dateFormat;
  };
  // 日期时间补零
  function padLeftZero(str) {
    return('00'+ str).substr(str.length);
  }
  setInterval(function() {
    // 获取当前时间
    let now = new Date()
    // 格式化当前时间
    let nowTimeStr = formatDate(new Date(now.getTime() - 1000), 'hhiiss')
    // 格式化下一秒时间
    let nextTimeStr = formatDate(now, 'hhiiss')
    // 将当前时间和下一秒时间逐位对比
    for(let i = 0; i < flipObjs.length; i++) {
      // 如果前后数字没有变化,则直接跳过,不翻牌
      if(nowTimeStr[i] === nextTimeStr[i]) {
        continue
      }
      // 传递前后牌的数字,进行向下翻牌动画
      flipObjs[i].flipDown('number'+ nowTimeStr[i], 'number'+ nextTimeStr[i])
    }
  }, 1000)
</script>

<style>

  .single-demo {
    margin: 50px auto;
    padding: 30px;
    width: 600px;
    text-align: center;
    border: solid 1px#999;
  }
  .btn-con {

    text-align: center;

  }
  .clock {
    text-align: center;
  }
  .clock em {
    display: inline-block;
    line-height: 102px;
    font-size: 66px;
    font-style: normal;
    vertical-align: top;
  }

  .flip {
    display: inline-block;

    position: relative;

    width: 60px;

    height: 100px;

    line-height: 100px;

    border: solid 1px #000;

    border-radius: 10px;

    background: #fff;

    font-size: 66px;

    color: #fff;

    box-shadow: 0 0 6px rgba(0, 0, 0, .5);

    text-align: center;

    font-family: "Helvetica Neue"

  }
  .flip .digital:before,

  .flip .digital:after {
    content: "";

    position: absolute;

    left: 0;

    right: 0;

    background: #000;

    overflow: hidden;

    box-sizing: border-box;
  }

  .flip .digital:before {
    top: 0;

    bottom: 50%;

    border-radius: 10px 10px 0 0;

    border-bottom: solid 1px #666;
  }

  .flip .digital:after {
    top: 50%;

    bottom: 0;

    border-radius: 0 0 10px 10px;

    line-height: 0;

  }



  /*向下翻*/
  .flip.down .front:before {
    z-index: 3;
  }
  .flip.down .back:after {
    z-index: 2;
    transform-origin: 50% 0%;
    transform: perspective(160px) rotateX(180deg);

  }
  .flip.down .front:after,
  .flip.down .back:before {
    z-index: 1;
  }



  /*向上翻*/
  .flip.up .front:after {
    z-index: 3;
  }
  .flip.up .back:before {
    z-index: 2;
    transform-origin: 50% 100%;
    transform: perspective(160px) rotateX(-180deg);
  }
  .flip.up .front:before,
  .flip.up .back:after {
    z-index: 1;
  }




  .flip.down.go .front:before {
    transform-origin: 50% 100%;
    animation: frontFlipDown 0.6s ease-in-out both;
    box-shadow: 0-2px 6px rgba(255, 255, 255, 0.3);
    backface-visibility: hidden;
  }
  .flip.down.go .back:after {

    animation: backFlipDown 0.6s ease-in-out both;

  }
  @keyframes frontFlipDown {
    0% {
      transform: perspective(160px) rotateX(0deg);
    }
    100% {
      transform: perspective(160px) rotateX(-180deg);
    }
  }
  @keyframes backFlipDown {
    0% {
      transform: perspective(160px) rotateX(180deg);
    }
    100% {
      transform: perspective(160px) rotateX(0deg);
    }
  }





  .flip .number0:before,

  .flip .number0:after {
    content: "0";

  }

  .flip .number1:before,

  .flip .number1:after {
    content: "1";

  }

  .flip .number2:before,

  .flip .number2:after {
    content: "2";

  }

  .flip .number3:before,

  .flip .number3:after {
    content: "3";

  }

  .flip .number4:before,

  .flip .number4:after {
    content: "4";

  }

  .flip .number5:before,

  .flip .number5:after {
    content: "5";

  }

  .flip .number6:before,

  .flip .number6:after {
    content: "6";

  }

  .flip .number7:before,

  .flip .number7:after {
    content: "7";

  }


  .flip .number8:before,

  .flip .number8:after {
    content: "8";

  }


  .flip .number9:before,

  .flip .number9:after {
    content: "9";

  }




</style>

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值