css3实现时钟翻转-程序员宅基地

技术标签: css  vue  js  html  javascript  

虽互不曾谋面,但希望能和您成为笔尖下的朋友

以读书,技术,生活为主,偶尔撒点鸡汤

不作,不敷衍,意在真诚吐露,用心分享

点击左上方,可关注本刊

标星公众号(ID:itclanCoder)

f176f98e04720ab5c80a508fa7afa5ba.gif

非常想念各位itclanCoder的粉友们,一直都没有更新博文,但我一直都是在的,感谢朋友还在

您可以在https://coder.itclan.cn的网站找到最新的内容,我会一直坚持下去的

今天给大家分享一个,翻转时钟的特效,具体Vue版本,React版本,原生javaScript版本如下所示:如有不清晰的,也可以点击文末左下方阅读原文

Vue版本实现

FlipClock组件

/*
 * 翻牌数字
 * @author:itclanCoder
 */
<template>
  <div class="FlipClock">
    <Flipper ref="flipperHour1" />
    <Flipper ref="flipperHour2" />
    <em>:</em>
    <Flipper ref="flipperMinute1" />
    <Flipper ref="flipperMinute2" />
    <em>:</em>
    <Flipper ref="flipperSecond1" />
    <Flipper ref="flipperSecond2" />
  </div>
</template>

<script>
import Flipper from './flipper'

export default {
  name: 'flipClock',
  data() {
    return {
      timer: null,
      flipObjs: []
    }
  },
  components: {
    Flipper
  },
  methods: {
    // 初始化数字
    init() {
      let now = new Date()
      let nowTimeStr = this.formatDate(new Date(now.getTime()), 'hhiiss')
       for (let i = 0; i < this.flipObjs.length; i++) {
         this.flipObjs[i].setFront(nowTimeStr[i])
       }
    },
    // 开始计时
    run() {
      this.timer = setInterval(() => {
        // 获取当前时间
        let now = new Date()
        let nowTimeStr = this.formatDate(new Date(now.getTime() - 1000), 'hhiiss')
        let nextTimeStr = this.formatDate(now, 'hhiiss')
        for (let i = 0; i < this.flipObjs.length; i++) {
          if (nowTimeStr[i] === nextTimeStr[i]) {
            continue
          }
          this.flipObjs[i].flipDown(
            nowTimeStr[i],
            nextTimeStr[i]
          )
        }
      }, 1000)
    },
    // 正则格式化日期
    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 : this.padLeftZero(str)
          )
        }
      }
      return dateFormat
    },
    // 日期时间补零
    padLeftZero(str) {
      return ('00' + str).substr(str.length)
    }
  },
  mounted() {
    this.flipObjs = [
      this.$refs.flipperHour1,
      this.$refs.flipperHour2,
      this.$refs.flipperMinute1,
      this.$refs.flipperMinute2,
      this.$refs.flipperSecond1,
      this.$refs.flipperSecond2
    ]
    this.init()
    this.run()
  }
}
</script>

<style>
.FlipClock {
    text-align: center;
}
.FlipClock .M-Flipper {
    margin: 0 3px;
}
.FlipClock em {
    display: inline-block;
    line-height: 102px;
    font-size: 66px;
    font-style: normal;
    vertical-align: top;
}
</style>

flipper组件组件

/*
 * 翻牌数字
 * @author:itclanCoder
 */
<template>
  <div class="M-Flipper" :class="[flipType, {'go': isFlipping}]">
    <div class="digital front" :class="_textClass(frontTextFromData)"></div>
    <div class="digital back" :class="_textClass(backTextFromData)"></div>
  </div>
</template>

<script>
export default {
  name: 'FlipClock',
  data() {
    return {
      isFlipping: false,
      flipType: 'down',
      frontTextFromData: 0,
      backTextFromData: 1
    }
  },
  props: {
    // front paper text
    // 前牌文字
    frontText: {
      type: [Number, String],
      default: 0
    },
    // back paper text
    // 后牌文字
    backText: {
      type: [Number, String],
      default: 1
    },
    // flipping duration, please be consistent with the CSS animation-duration value.
    // 翻牌动画时间,与CSS中设置的animation-duration保持一致
    duration: {
      type: Number,
      default: 600
    }
  },
  methods: {
    _textClass(number) {
      return 'number' + number
    },
    _flip(type, front, back) {
      // 如果处于翻转中,则不执行
      if (this.isFlipping) {
        return false
      }
      this.frontTextFromData = front
      this.backTextFromData = back
      // 根据传递过来的type设置翻转方向
      this.flipType = type
      // 设置翻转状态为true
      this.isFlipping = true
      setTimeout(() => {
        // 设置翻转状态为false
        this.isFlipping = false
        this.frontTextFromData = back
      }, this.duration)
    },
    // 下翻牌
    flipDown(front, back) {
      this._flip('down', front, back)
    },
    // 上翻牌
    flipUp(front, back) {
      this._flip('up', front, back)
    },
    // 设置前牌文字
    setFront(text) {
        this.frontTextFromData = text
    },
    // 设置后牌文字
    setBack(text) {
        this.backTextFromData = text
    }
  },
  created() {
      this.frontTextFromData = this.frontText
      this.backTextFromData = this.backText
  }
}
</script>

<style>
.M-Flipper {
  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, 0.5);
  text-align: center;
  font-family: 'HYLeMiaoTiW';
}

@media screen and (min-width: 375px) and (max-width: 768px){
   .M-Flipper {
       width: 35px;
      font-size: 40px;
   }
}

@media screen and (max-width: 320px){
   .M-Flipper {
       width: 25px;
      font-size: 40px;
   }
}

@media screen and (min-width: 320px) and (max-width: 375px){
   .M-Flipper {
      width: 27px;
      font-size: 40px;
   }
}

.M-Flipper .digital:before,
.M-Flipper .digital:after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  background: #000;
  overflow: hidden;
  box-sizing: border-box;
}

.M-Flipper .digital:before {
  top: 0;
  bottom: 50%;
  border-radius: 10px 10px 0 0;
  border-bottom: solid 1px #666;
}

.M-Flipper .digital:after {
  top: 50%;
  bottom: 0;
  border-radius: 0 0 10px 10px;
  line-height: 0;
}

/*向下翻*/
.M-Flipper.down .front:before {
  z-index: 3;
}

.M-Flipper.down .back:after {
  z-index: 2;
  transform-origin: 50% 0%;
  transform: perspective(160px) rotateX(180deg);
}

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

.M-Flipper.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;
}

.M-Flipper.down.go .back:after {
  animation: backFlipDown 0.6s ease-in-out both;
}

/*向上翻*/
.M-Flipper.up .front:after {
  z-index: 3;
}

.M-Flipper.up .back:before {
  z-index: 2;
  transform-origin: 50% 100%;
  transform: perspective(160px) rotateX(-180deg);
}

.M-Flipper.up .front:before,
.M-Flipper.up .back:after {
  z-index: 1;
}

.M-Flipper.up.go .front:after {
  transform-origin: 50% 0;
  animation: frontFlipUp 0.6s ease-in-out both;
  box-shadow: 0 2px 6px rgba(255, 255, 255, 0.3);
  backface-visibility: hidden;
}

.M-Flipper.up.go .back:before {
  animation: backFlipUp 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);
  }
}

@keyframes frontFlipUp {
  0% {
    transform: perspective(160px) rotateX(0deg);
  }

  100% {
    transform: perspective(160px) rotateX(180deg);
  }
}

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

  100% {
    transform: perspective(160px) rotateX(0deg);
  }
}

.M-Flipper .number0:before,
.M-Flipper .number0:after {
  content: '0';
}

.M-Flipper .number1:before,
.M-Flipper .number1:after {
  content: '1';
}

.M-Flipper .number2:before,
.M-Flipper .number2:after {
  content: '2';
}

.M-Flipper .number3:before,
.M-Flipper .number3:after {
  content: '3';
}

.M-Flipper .number4:before,
.M-Flipper .number4:after {
  content: '4';
}

.M-Flipper .number5:before,
.M-Flipper .number5:after {
  content: '5';
}

.M-Flipper .number6:before,
.M-Flipper .number6:after {
  content: '6';
}

.M-Flipper .number7:before,
.M-Flipper .number7:after {
  content: '7';
}

.M-Flipper .number8:before,
.M-Flipper .number8:after {
  content: '8';
}

.M-Flipper .number9:before,
.M-Flipper .number9:after {
  content: '9';
}
</style>

React版本实现

Filpper.js

/*
 * 翻牌数字
 * @author:itclanCoder-React
 */
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './flipper.css'

class Flipper extends Component {
    constructor(props) {
        super(props)
        this.state = {
            isFlipping: false,
            flipType: 'down',
            frontTextFromData: 0,
            backTextFromData: 1
        }
    }

    render() {
        const { isFlipping, flipType, frontTextFromData, backTextFromData } = this.state
        return (
            <div className={['M-Flipper', flipType, isFlipping?'go':null].join(' ')}>
                <div className={'digital front ' + this._textClass(frontTextFromData)}></div>
                <div className={'digital back ' + this._textClass(backTextFromData)}></div>
            </div>
        )
    }

    // componentDidMount() {
    //     this.props.onRef(this)
    // }
    test() {
        console.log('test')   
    }
    _textClass(number) {
        return 'number' + number
    }
    _flip(type, front, back) {
        // 如果处于翻转中,则不执行
        if (this.isFlipping) {
            return false
        }
        this.setState({
            frontTextFromData: front,
            backTextFromData: back,
            // 根据传递过来的type设置翻转方向
            flipType: type,
            // 设置翻转状态为true
            isFlipping: true
        })
        setTimeout(() => {
            this.setState({
                frontTextFromData: back,
                isFlipping: false
            })
        }, this.props.duration)
    }
    // 下翻牌
    flipDown(front, back) {
        this._flip('down', front, back)
    }
    // 上翻牌
    flipUp(front, back) {
        this._flip('up', front, back)
    }
    // 设置前牌文字
    setFront(text) {
        this.setState({
            frontTextFromData: text
        })
    }
    // 设置后牌文字
    setBack(text) {
        this.setState({
            backTextFromData: text
        })
    }
}

// props类型校验
Flipper.propTypes = {
    frontText: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string
    ]),
    backText: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string
    ]),
    duration: PropTypes.number
}

// props默认值
Flipper.defaultProps = {
    // front paper text
    // 前牌文字
    frontText: 0,
    // back paper text
    // 后牌文字
    backText: 1,
    // flipping duration, please be consistent with the CSS animation-duration value.
    // 翻牌动画时间,与CSS中设置的animation-duration保持一致
    duration: 600
}

export default Flipper

FilpClock.js

import React, { Component } from 'react'
import Flipper from './Flipper'
import './flipClock.css'

class FlipClock extends Component {
    constructor(props) {
        super(props)
        this.timer = null
        this.flipObjs = []
    }

    render() {
        return (
            <div className="FlipClock">
                <Flipper ref="flipperHour1" />
                <Flipper ref="flipperHour2" />
                <em>:</em>
                <Flipper ref="flipperMinute1" />
                <Flipper ref="flipperMinute2" />
                <em>:</em>
                <Flipper ref="flipperSecond1" />
                <Flipper ref="flipperSecond2" />
            </div>
        )
    }

    componentDidMount() {
        this.flipObjs = [
            this.refs.flipperHour1,
            this.refs.flipperHour2,
            this.refs.flipperMinute1,
            this.refs.flipperMinute2,
            this.refs.flipperSecond1,
            this.refs.flipperSecond2
        ]
        this.init()
        this.run()
    }

    // 初始化数字
    init() {
        let now = new Date()
        let nowTimeStr = this.formatDate(new Date(now.getTime()), 'hhiiss')
        for (let i = 0; i < this.flipObjs.length; i++) {
            this.flipObjs[i].setFront(nowTimeStr[i])
        }
    }
    // 开始计时
    run() {
        this.timer = setInterval(() => {
            // 获取当前时间
            let now = new Date()
            let nowTimeStr = this.formatDate(new Date(now.getTime() - 1000), 'hhiiss')
            let nextTimeStr = this.formatDate(now, 'hhiiss')
            for (let i = 0; i < this.flipObjs.length; i++) {
                if (nowTimeStr[i] === nextTimeStr[i]) {
                    continue
                }
                this.flipObjs[i].flipDown(
                    nowTimeStr[i],
                    nextTimeStr[i]
                )
            }
        }, 1000)
    }
    // 正则格式化日期
    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 : this.padLeftZero(str)
                )
            }
        }
        return dateFormat
    }
    // 日期时间补零
    padLeftZero(str) {
        return ('00' + str).substr(str.length)
    }
}
export default FlipClock

flipClock.css

.FlipClock {
    text-align: center;
}

.FlipClock .M-Flipper {
    margin: 0 3px;
}
  
.FlipClock em {
    display: inline-block;
    line-height: 102px;
    font-size: 66px;
    font-style: normal;
    vertical-align: top;
}

flipper.css

.M-Flipper {
    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, 0.5);
    text-align: center;
    font-family: 'Helvetica Neue';
  }
  
  .M-Flipper .digital:before,
  .M-Flipper .digital:after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    background: #000;
    overflow: hidden;
    box-sizing: border-box;
  }
  
  .M-Flipper .digital:before {
    top: 0;
    bottom: 50%;
    border-radius: 10px 10px 0 0;
    border-bottom: solid 1px #666;
  }
  
  .M-Flipper .digital:after {
    top: 50%;
    bottom: 0;
    border-radius: 0 0 10px 10px;
    line-height: 0;
  }
  
  /*向下翻*/
  .M-Flipper.down .front:before {
    z-index: 3;
  }
  
  .M-Flipper.down .back:after {
    z-index: 2;
    transform-origin: 50% 0%;
    transform: perspective(160px) rotateX(180deg);
  }
  
  .M-Flipper.down .front:after,
  .M-Flipper.down .back:before {
    z-index: 1;
  }
  
  .M-Flipper.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;
  }
  
  .M-Flipper.down.go .back:after {
    animation: backFlipDown 0.6s ease-in-out both;
  }
  
  /*向上翻*/
  .M-Flipper.up .front:after {
    z-index: 3;
  }
  
  .M-Flipper.up .back:before {
    z-index: 2;
    transform-origin: 50% 100%;
    transform: perspective(160px) rotateX(-180deg);
  }
  
  .M-Flipper.up .front:before,
  .M-Flipper.up .back:after {
    z-index: 1;
  }
  
  .M-Flipper.up.go .front:after {
    transform-origin: 50% 0;
    animation: frontFlipUp 0.6s ease-in-out both;
    box-shadow: 0 2px 6px rgba(255, 255, 255, 0.3);
    backface-visibility: hidden;
  }
  
  .M-Flipper.up.go .back:before {
    animation: backFlipUp 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);
    }
  }
  
  @keyframes frontFlipUp {
    0% {
      transform: perspective(160px) rotateX(0deg);
    }
  
    100% {
      transform: perspective(160px) rotateX(180deg);
    }
  }
  
  @keyframes backFlipUp {
    0% {
      transform: perspective(160px) rotateX(-180deg);
    }
  
    100% {
      transform: perspective(160px) rotateX(0deg);
    }
  }
  
  .M-Flipper .number0:before,
  .M-Flipper .number0:after {
    content: '0';
  }
  
  .M-Flipper .number1:before,
  .M-Flipper .number1:after {
    content: '1';
  }
  
  .M-Flipper .number2:before,
  .M-Flipper .number2:after {
    content: '2';
  }
  
  .M-Flipper .number3:before,
  .M-Flipper .number3:after {
    content: '3';
  }
  
  .M-Flipper .number4:before,
  .M-Flipper .number4:after {
    content: '4';
  }
  
  .M-Flipper .number5:before,
  .M-Flipper .number5:after {
    content: '5';
  }
  
  .M-Flipper .number6:before,
  .M-Flipper .number6:after {
    content: '6';
  }
  
  .M-Flipper .number7:before,
  .M-Flipper .number7:after {
    content: '7';
  }
  
  .M-Flipper .number8:before,
  .M-Flipper .number8:after {
    content: '8';
  }
  
  .M-Flipper .number9:before,
  .M-Flipper .number9:after {
    content: '9';
  }

原生javaScript实现

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Flipper</title>
</head>

<body>
    <div class="single-demo">
        <div class="flip down" id="flip">
            <div class="digital front number0"></div>
            <div class="digital back number1"></div>
        </div>
        <div class="btn-con">
            <button id="btn1">向下翻+1</button>
            <button id="btn2">向上翻-1</button>
        </div>
    </div>
    <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>
<script>
var flip = document.getElementById('flip')
var backNode = document.querySelector('.back')
var frontNode = document.querySelector('.front')
var btn1 = document.getElementById('btn1')
var btn2 = document.getElementById('btn2')
btn1.addEventListener('click', function() {
    flipDown();
})
btn2.addEventListener('click', function() {
    flipUp();
})
// 当前数字
var count = 0
// 是否正在翻转(防止翻转未结束就进行下一次翻转)
var isFlipping = false

// 向下翻转+1
function flipDown() {
    // 如果处于翻转中,则不执行
    if (isFlipping) {
        return false
    }
    // 设置前牌的文字
    frontNode.setAttribute('class', 'digital front number' + count)
    // 计算后牌文字(越界判断)
    var nextCount = count >= 9 ? 0 : (count + 1)
    // 设置后牌的文字
    backNode.setAttribute('class', 'digital back number' + nextCount)
    // 添加go,执行翻转动画
    flip.setAttribute('class', 'flip down go')
    // 当翻转态设置为true
    isFlipping = true
    // 翻转结束后,恢复状态
    setTimeout(function() {
        // 去掉go
        flip.setAttribute('class', 'flip down')
        // 当翻转态设置为false
        isFlipping = false
        // 设置前牌文字为+1后的数字
        frontNode.setAttribute('class', 'digital front number' + nextCount)
        // 更新当前文字
        count = nextCount
    }, 1000)
}
// 向上翻转-1(同理,注释略)
function flipUp() {
    if (isFlipping) {
        return false
    }
    frontNode.setAttribute('class', 'digital front number' + count)
    var nextCount = count <= 0 ? 9 : (count - 1)
    backNode.setAttribute('class', 'digital back number' + nextCount)
    flip.setAttribute('class', 'flip up go')
    isFlipping = true
    setTimeout(function() {
        flip.setAttribute('class', 'flip up')
        isFlipping = false
        frontNode.setAttribute('class', 'digital front number' + nextCount)
        count = nextCount
    }, 1000)
}


/* 时钟代码 */
// 时钟翻牌
function Flipper(config) {
    // 默认配置
    this.config = {
        // 时钟模块的节点
        node: null,
        // 初始前牌文字
        frontText: 'number0',
        // 初始后牌文字
        backText: 'number1',
        // 翻转动画时间(毫秒,与翻转动画CSS 设置的animation-duration时间要一致)
        duration: 600
    }
    // 节点的原本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.push(new Flipper({
        // 每个flipper实例按数组顺序与翻板DOM的顺序一一对应
        node: flips[i],
        // 按数组顺序取时间字符串对应位置的数字
        frontText: 'number' + nowTimeStr[i],
        backText: 'number' + nextTimeStr[i]
    }))
}


// 开始计时
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)

//正则格式化日期
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);
}

</script>
<style>
.single-demo {
    margin: 50px auto;
    padding: 30px;
    width: 600px;
    text-align: center;
    border: solid 1px #999;
}


.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.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;
}

/*向上翻*/
.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.up.go .front:after {
    transform-origin: 50% 0;
    animation: frontFlipUp 0.6s ease-in-out both;
    box-shadow: 0 2px 6px rgba(255, 255, 255, 0.3);
    backface-visibility: hidden;
}

.flip.up.go .back:before {
    animation: backFlipUp 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);
    }
}


@keyframes frontFlipUp {
    0% {
        transform: perspective(160px) rotateX(0deg);
    }

    100% {
        transform: perspective(160px) rotateX(180deg);
    }
}

@keyframes backFlipUp {
    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";
}

.clock {
    text-align: center;
    margin-bottom: 200px;
}

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

</html>

总结

大家看到的360画报,屏保的数字时钟,就是参考如上代码实现的

c4e2ce414af0b0d0fef0620f04e927e5.png

记账就用轻记账


75d016698cba2f18bda6041065ff4fa7.png

公众号(ID:itclanCoder)

码能让您早脱菜籍,文能让您洗净铅华

文章都看完了23d6018d1ddf3863fe7a24f0d879a1cd.gif不点个在看吗

4150d46a481b5689b25a37e34d05797c.gif

戳原文,即可阅读原文哦

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wzc_coder/article/details/121369063

智能推荐

【2024】MathorCupA 题| 移动通信网络中 PCI 规划问题(思路、代码持续更新......)-程序员宅基地

文章浏览阅读685次,点赞5次,收藏6次。物理小区识别码(PCI)规划是移动通信网络中下行链路层上,对各覆盖小区编号进行合理配置,以避免PCI冲突、PCI混淆以及PCI 模3干扰等现象。PCI 规划对于减少物理层的小区间互相干(ICI),增加物理下行控制信道(PDCCH)的吞吐量有着重要的作用,尤其是对于基站小区覆盖边缘的用户和发生信号切换的用户,能有效地降低信号干扰,提升用户的体验。

STM32CubeMX ULN2003步进电机驱动_stm32 步进电机-程序员宅基地

文章浏览阅读690次,点赞10次,收藏16次。根据数据表,当28BYJ-48电机在全步模式下运行时,每步对应于11.25°的旋转。这意味着每转有32步 (360°/11.25° = 32)。链接: https://pan.baidu.com/s/1iucYeZGygwHi3DYeds4gqA?电机转一圈有360°,那么转一圈的脉冲数 = 360 / 5.625 * 64 = 4096 个脉冲。28BYJ-48是一款5线单极步进电机,运行电压为5V。如上图所示,步距角=5.625°/64。一、28BYJ-48 步进电机。二、CubeMX配置。_stm32 步进电机

论文阅读 Vision Transformer - VIT_vision transformer论文-程序员宅基地

文章浏览阅读712次,点赞8次,收藏9次。通过将图像切成patch线形层编码成token特征编码的方法,用transformer的encoder来做图像分类multihead和我原有的理解偏差修正。我以为的是QKV会有N块相同的copy(),每一份去做后续的linear等操作。代码里是直接用linear将QKV分为一整个大块,用permute/rearrange的操作切成了N块,f(Q,K)之后再恢复成一整个大块,很强。_vision transformer论文

es安装后的配置_es安装完-程序员宅基地

文章浏览阅读180次。今天在安装好了es,启动和配置花了点时间,这里记录一下。配置elasticsearch.yml,参数大致如下:属性说明cluster.name集群名称,默认elasticsearchnode.name节点名称node.attr.rack节点服务器所在的机架信息path.data索引数据存储路径path.log日志存储路径bootstr..._es安装完

php程序控制打印机自动打印_php实现打印机打印-程序员宅基地

文章浏览阅读1.2k次。【代码】php程序控制打印机自动打印。_php实现打印机打印

[论文速度] 超分系列:基于频率分离的图像超分辨率算法 两篇 ICCVW 2019 和 CVPRW 2020_frequency separation for real-world super-resoluti-程序员宅基地

文章浏览阅读2.9k次。Frequency Separation for Real-World Super-Resolution[PAPER]_frequency separation for real-world super-resolution

随便推点

论文参考文献的引用规则-程序员宅基地

文章浏览阅读927次,点赞19次,收藏24次。在科技论文中,凡是引用前人(包括作者自己过去)已发表的文献中的观点、数据和材料等,都要对它们在文中出现的地方予以标明,并在文末(致谢段之后)列出参考文献表。这项工作叫作参考文献著录。科学研究具有继承性,需要研究者充分了解前人的工作,吸收前人的研究精华,开拓创新。因此,撰写科技论文不可避免地要引用、参考别人的文献。论文列参考文献,一是交代作者的研究背景,反映出真实的科学依据,介绍他人的研究成果,又便于感兴趣的读者查阅,进行深入研究。

idea开发android studio插件,打印日志_idea 导出安卓日志-程序员宅基地

文章浏览阅读2.4k次。插件项目中需要打印的地方全部使用idea的logger类import com.intellij.openapi.diagnostic.Logger;/** * @Author: XIELINHUA * @Date: 2019/7/23 12:28 */public class ApkTools extends DumbAwareAction { private static..._idea 导出安卓日志

深入理解python的迭代器,生成器,可迭代对象区别-程序员宅基地

文章浏览阅读280次。要先搞清楚这三者区别首先需要给文章点个赞。呸呸呸,说错了,需要先弄明白什么是迭代,先补充一下迭代的概念在进入正题的讲解。迭代:像list,tuple,dict这样的数据类型,我们通过for...in...语法从中依次拿到数据进行使用,这样的遍历过程,就叫做迭代正文开始1,可迭代对象简单来说,某个对象内部具有__iter__方法,那么它就是可迭代对象(Iterable)。全剧终!事情..._请说一下迭代器,可迭代对象,生成器的定义,区别,优缺点。

2021年关于Delphi/Object Pascal编程语言的现状和历史-程序员宅基地

文章浏览阅读8.3k次,点赞4次,收藏8次。关于Delphi/Object PascalDelphi是一种基于 Object Pascal 的编程语言,用于桌面、移动、Web 和控制台软件开发。在 1995 年首次亮相之前的最初开发过程中,Delphi 是一种尚未命名的产品的代号。function DigitSum(AValue: Int64): Integer;begin if AValue mod 10 = AValue then Result := AValue else Result := (AVa.._delphi/object pascal

el-upload 上传文件,去掉默认动画效果。_去除el-upload上传动画-程序员宅基地

文章浏览阅读3.7k次,点赞5次,收藏5次。el-upload 上传文件,去掉默认动画效果。_去除el-upload上传动画

CentOS7离线安装Mysql_centos离线安装mysql5.7-程序员宅基地

文章浏览阅读776次。CenOS7 离线安装Mysql5.7 (rpm)_centos离线安装mysql5.7

推荐文章

热门文章

相关标签