使用html,js,css实现电子表翻页效果

1.html代码布局index.html文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <link rel="stylesheet" href="style.css" />
  </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>

    <script src="index.js"></script>
  </body>
</html>

2.js文件index.js

var flip = document.getElementById('flip')
var backNode = document.querySelector('.back')
var frontNode = document.querySelector('.front')
// 当前数字
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)
}

var btn1 = document.getElementById('btn1')
var btn2 = document.getElementById('btn2')
btn1.addEventListener('click', function () {
    flipDown();
})
btn2.addEventListener('click', function () {
    flipUp();
})
/* 时钟代码 */
// 时钟翻牌
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);
}

3.css文件style.css

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

最终效果图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BUG_Jia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值