CSS3实现类似翻书效果的过渡动画的示例代码


Posted in HTML / CSS onSeptember 06, 2019

在VUE实战项目的中有一个加载推荐书籍的过渡动画,在项目中是使用JS实现。

CSS3实现类似翻书效果的过渡动画的示例代码

当时看视频大概一个小时作用,拆分动画逻辑,写代码更是耗时。后来自己想着能不能用CSS动画直接写出来,折腾了半天,终于算是实现了。
可以查看加载动画地址

/*首先是DOM结构,不能像js一样分左右两边,正面翻为反面还要改变z-index,按照同样的布局也尝试过,没有用CSS动画写出来,逻辑太复杂。
这是一个类似翻书一样的动画效果,想着结构分为一页一页,一页旋转180°,完成时再改变“这一页”的z-index。每个page类的before伪类为正面,after伪类为反面;分解5页的在每一秒的动画,写出各自的animation。
*/
<div class="card">
    <div class="page"></div>
    <div class="page"></div>
    <div class="page"></div>
    <div class="page"></div>
    <div class="page"></div>
</div>



        .card{
            width: 50px;
            height: 50px;
            opacity: 0;
            position: relative;
            animation: .5s all;
        }
        .card .page{
            width: 50%;
            height: 100%;
            position: absolute;
            left: 50%;
            top: 0;
            transform-style: preserve-3d;
            transform-origin: left;
        }
        .card .page::before, .card .page::after{
            content: '';
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            border-radius: 0px 50px 50px 0px;
        }
        .card .page::after{
            border-radius: 50px 0px 0px 50px;
        }
        .card .page:nth-child(1){
            animation: animation1 2s linear infinite;
        }
        .card .page:nth-child(2){
            animation: animation2 2s linear infinite;
        }
        .card .page:nth-child(3){
            animation: animation3 2s linear infinite;
        }
        .card .page:nth-child(4){
            animation: animation4 2s linear infinite;
        }
        .card .page:nth-child(5){
            animation: animation5 2s linear infinite;
        }
        .card .page:nth-child(1)::before{
            background: aqua url("./images/star-right.png") center left/50% 50% no-repeat;
        }
        .card .page:nth-child(1)::after{
            background: hotpink url("./images/compass-left.png") center right/50% 50% no-repeat;
            transform: rotateY(180deg);
        }
        .card .page:nth-child(2)::before{
            background: hotpink url("./images/compass-right.png") center left/50% 50% no-repeat;
        }
        .card .page:nth-child(2)::after{
            background: coral url("./images/crown-left.png") center right/50% 50% no-repeat;
            transform: rotateY(180deg);
        }
        .card .page:nth-child(3)::before{
            background: coral url("./images/crown-right.png") center left/50% 50% no-repeat;
        }
        .card .page:nth-child(3)::after{
            background: cyan url("./images/gift-left.png") center right/50% 50% no-repeat;
            transform: rotateY(180deg);
        }
        .card .page:nth-child(4)::before{
            background: cyan url("./images/gift-right.png") center left/50% 50% no-repeat;
        }
        .card .page:nth-child(4)::after{
            background: yellowgreen url("./images/heart-left.png") center right/50% 50% no-repeat;
            transform: rotateY(180deg);
        }
        .card .page:nth-child(5)::before{
            background: yellowgreen url("./images/heart-right.png") center left/50% 50% no-repeat;
        }
        .card .page:nth-child(5)::after{
            background: aqua url("./images/star-left.png") center right/50% 50% no-repeat;
            transform: rotateY(180deg);
        }
@keyframes animation1 {
            0%{
                z-index: 1;
                transform: rotateY(180deg);
            }
            20%{
                z-index: 1;
                transform: rotateY(180deg);
            }
            40%{
                z-index: 3;
                transform: rotateY(360deg);
            }
            60%{
                z-index: 4;
                transform: rotateY(360deg);
            }
            60.0001%{
                z-index: 4;
                transform: rotateY(0deg);
            }
            80%{
                z-index: 5;
                transform: rotateY(0deg);
            }
            100%{
                z-index: 4;
                transform: rotateY(180deg);
            }
        }
        @keyframes animation2 {
            0%{
                z-index: 5;
                transform: rotateY(0deg);
            }
            20%{
                z-index: 4;
                transform: rotateY(180deg);
            }
            40%{
                z-index: 1;
                transform: rotateY(180deg);
            }
            60%{
                z-index: 3;
                transform: rotateY(360deg);
            }
            80%{
                z-index: 4;
                transform: rotateY(360deg);
            }
            100%{
                z-index: 5;
                transform: rotateY(360deg);
            }
        }
        @keyframes animation3 {
            0%{
                z-index: 4;
                transform: rotateY(0deg);
            }
            20%{
                z-index: 5;
                transform: rotateY(0deg);
            }
            40%{
                z-index: 4;
                transform: rotateY(180deg);
            }
            60%{
                z-index: 1;
                transform: rotateY(180deg);
            }
            80%{
                z-index: 3;
                transform: rotateY(360deg);
            }
            100%{
                z-index: 4;
                transform: rotateY(360deg);
            }
        }
        @keyframes animation4 {
            0%{
                z-index: 3;
                transform: rotateY(0deg);
            }
            20%{
                z-index: 4;
                transform: rotateY(0deg);
            }
            40%{
                z-index: 5;
                transform: rotateY(0deg);
            }
            60%{
                z-index: 4;
                transform: rotateY(180deg);
            }
            80%{
                z-index: 1;
                transform: rotateY(180deg);
            }
            100%{
                z-index: 3;
                transform: rotateY(360deg);
            }
        }
        @keyframes animation5 {
            0%{
                z-index: 2;
                transform: rotateY(0deg);
            }
            20%{
                z-index: 3;
                transform: rotateY(0deg);
            }
            40%{
                z-index: 4;
                transform: rotateY(0deg);
            }
            60%{
                z-index: 5;
                transform: rotateY(0deg);
            }
            80%{
                z-index: 4;
                transform: rotateY(180deg);
            }
            100%{
                z-index: 1;
                transform: rotateY(180deg);
            }
        }

再贴出JS实现:

<div class="flap-card" v-for="(item, index) in flapCardList" :key="index" :style="{zIndex: item.zIndex}">
    <div class="flap-card-circle">
        <div class="flap-card-semi-circle flap-card-semi-circle-left" :style="semiCircleStyle(item, 'left')"
           ref="left"></div>
        <div class="flap-card-semi-circle flap-card-semi-circle-right" :style="semiCircleStyle(item, 'right')"
           ref="right"></div>
    </div>
</div>
data() {
  return {
    front: 0,
    back: 1,
  }
},
methods: {
    // flapCardList是存储着图片信息的对象数组,通过v-for循环和semiCircleStyle方法设置5组图片的背景。
    semiCircleStyle(item, dir) {
        return {
          backgroundColor: `rgb(${item.r}, ${item.g}, ${item.b})`,
          backgroundSize: item.backgroundSize,
          backgroundImage: dir === 'left' ? item.imgLeft : item.imgRight
        }
    },
    rotate(index, type) {
        //卡牌翻转,"front"选择右边卡片,否则选择左边卡片;然后改变dom元素的样式
        const item = this.flapCardList[index]
        let dom
        if (type === 'front') {
          dom = this.$refs.right[index]
        } else {
          dom = this.$refs.left[index]
        }
        dom.style.transform = `rotateY(${item.rotateDegree}deg)`
        dom.style.backgroundColor = `rgb(${item.r}, ${item._g}, ${item.b})`
    },
    flapCardRotate() {
    //首先是翻转函数,每次翻转分正反两面。当动画至90°时,改变背面的z-index值,每一帧动画完毕执行rotate函数以改变样式。
    const frontFlapCard = this.flapCardList[this.front]
    const backFlapCard = this.flapCardList[this.back]
    frontFlapCard.rotateDegree += 10
    frontFlapCard._g -= 5
    backFlapCard.rotateDegree -= 10
    if (backFlapCard.rotateDegree < 90) {
      backFlapCard._g += 5
    }
    if (frontFlapCard.rotateDegree === 90 && backFlapCard.rotateDegree === 90) {
      backFlapCard.zIndex += 2
    }
    this.rotate(this.front, 'front')
    this.rotate(this.back, 'back')
    if (frontFlapCard.rotateDegree === 180 && backFlapCard.rotateDegree === 0) {
      this.next()
    }
  },
  prepare() {
    const backFlapCard = this.flapCardList[this.back]
    backFlapCard.rotateDegree = 180
    backFlapCard._g = backFlapCard.g - 5 * 9
    this.rotate(this.back, 'back')
  },
  next() {
    const frontFlapCard = this.flapCardList[this.front]
    const backFlapCard = this.flapCardList[this.back]
    frontFlapCard.rotateDegree = 0
    backFlapCard.rotateDegree = 0
    frontFlapCard._g = frontFlapCard.g
    backFlapCard._g = backFlapCard.g
    this.rotate(this.front, 'front')
    this.rotate(this.back, 'back')
    this.front++
    this.back++
    const len = this.flapCardList.length
    if (this.front >= len) {
      this.front = 0
    }
    if (this.back >= len) {
      this.back = 0
    }
    // 动态设置zIndex
    // 100 -> 96
    // 99 -> 100
    // 98 -> 99
    // 97 -> 98
    // 96 -> 97
    // (0 - 1 + 5) % 5 = 4
    // (1 - 1 + 5) % 5 = 0
    this.flapCardList.forEach((item, index) => {
      item.zIndex = 100 - ((index - this.front + len) % len)
    })
    this.prepare()
  },
  startFlapCardAnimation() {
    this.prepare()
    this.task = setInterval(() => {
      this.flapCardRotate()
    }, this.intervalTime)
  },
  stopAnimation() {
    //动画停止时,清除所有setTimeout;否则再次进入动画出现错误。
    if (this.task) {
      clearInterval(this.task)
    }
    if (this.timeout) {
      clearTimeout(this.timeout)
    }
    if (this.timeout2) {
      clearTimeout(this.timeout2)
    }
    this.reset()
  },
  runAnimation() {
    //点击推荐,则动画开始(vuex中定义了flapCardVisible,watch监听其变化,为TRUE则动画开始)
    this.runFlapCardAnimation = true
    this.timeout = setTimeout(() => {
      this.startFlapCardAnimation()
      this.startPointAnimation()
    }, 300)
    this.timeout2 = setTimeout(() => {
      this.stopAnimation()
      //2500ms过渡动画结束,推荐图书显示
      this.runBookCardAnimation = true
    }, 2500)
  },
  watch: {
      flapCardVisible(v) {
        if (v) {
          this.runAnimation()
        }
      }
  },
}

写在最后:初次写CSS动画的总结,而且感觉自己的实现并不是很好,应该有更简单些的实现方法。从DOM结构设计,到动画过程分析还欠缺很多。并且一个动画如此费时,在实际项目中应该得不偿失。还是需要大量的练习,经常练手才可熟能生巧。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

HTML / CSS 相关文章推荐
css3+jq创作含苞待放的荷花
Feb 20 HTML / CSS
CSS3实现多背景展示效果通过CSS3定位多张背景
Aug 10 HTML / CSS
css3的transform中scale缩放详解
Dec 08 HTML / CSS
使用css3实现的tab选项卡代码分享
Dec 09 HTML / CSS
一款纯css3实现的竖形二级导航的实例教程
Dec 11 HTML / CSS
websocket+sockjs+stompjs详解及实例代码
Nov 30 HTML / CSS
Html5 实现微信分享及自定义内容的流程
Aug 20 HTML / CSS
html5 Canvas画图教程(6)—canvas里画曲线之arcTo方法
Jan 09 HTML / CSS
html5的画布canvas——画出简单的矩形、三角形实例代码
Jun 09 HTML / CSS
phonegap常用事件总结(必看篇)
Mar 31 HTML / CSS
HTML5 Canvas实现360度全景图的示例代码
Jan 29 HTML / CSS
canvas实现手机的手势解锁的步骤详细
Mar 16 HTML / CSS
CSS3动画之利用requestAnimationFrame触发重新播放功能
Sep 11 #HTML / CSS
css3 响应式媒体查询的示例代码
Sep 25 #HTML / CSS
移动端rem布局的两种实现方法
Jan 03 #HTML / CSS
css3实现波纹特效、H5实现动态波浪效果
Jan 31 #HTML / CSS
css3动画过渡实现鼠标跟随导航效果
Feb 08 #HTML / CSS
CSS3 calc()会计算属性详解
Feb 27 #HTML / CSS
CSS3 clip-path 用法介绍详解
Mar 01 #HTML / CSS
You might like
PHP获取网站域名和地址的代码
2008/08/17 PHP
php页面缓存ob系列函数介绍
2012/10/18 PHP
解析mysql 表中的碎片产生原因以及清理
2013/06/22 PHP
php-perl哈希算法实现(times33哈希算法)
2013/12/30 PHP
php魔术变量用法实例详解
2014/11/13 PHP
php使用cookie显示用户上次访问网站日期的方法
2015/01/26 PHP
判断、添加和删除WordPress置顶文章的相关PHP函数小结
2015/12/10 PHP
php基于PDO实现功能强大的MYSQL封装类实例
2017/02/27 PHP
如何确保JavaScript的执行顺序 之实战篇
2011/03/03 Javascript
基于jQuery的输入框在光标位置插入内容, 并选中
2011/10/29 Javascript
js动态拼接正则表达式的两种方法
2014/03/04 Javascript
JavaScript之数组(Array)详解
2015/04/01 Javascript
JavaScript几种数组去掉重复值的方法推荐
2016/04/12 Javascript
xtemplate node.js 的使用方法实例解析
2016/08/22 Javascript
详解nodejs 文本操作模块-fs模块(三)
2016/12/22 NodeJs
nodejs中向HTTP响应传送进程的输出
2017/03/19 NodeJs
为什么我们要做三份 Webpack 配置文件
2017/09/18 Javascript
浅谈PDF.js使用心得
2018/06/07 Javascript
解决vue中修改了数据但视图无法更新的情况
2018/08/27 Javascript
JS原型对象操作实例分析
2020/06/06 Javascript
[40:31]Secret vs Alliacne 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
利用python微信库itchat实现微信自动回复功能
2017/05/18 Python
python爬虫之urllib3的使用示例
2018/07/09 Python
python 获取utc时间转化为本地时间的方法
2018/12/31 Python
Python实现根据日期获取当天凌晨时间戳的方法示例
2019/04/09 Python
实例详解python函数的对象、函数嵌套、名称空间和作用域
2019/05/31 Python
HTML5新表单元素_动力节点Java学院整理
2017/07/12 HTML / CSS
HTML5 input placeholder 颜色修改示例
2014/05/30 HTML / CSS
英国设计师泳装、沙滩装和比基尼在线精品店:Beach Cafe
2019/08/28 全球购物
校友会欢迎辞
2014/01/13 职场文书
2014年公务员思想汇报范文:全心全意为人民服务
2014/03/06 职场文书
保险经纪人求职信
2014/03/11 职场文书
激励员工的口号
2014/06/16 职场文书
研究生导师推荐信
2014/09/06 职场文书
人事任命书范本
2015/09/21 职场文书
使用 JavaScript 制作页面效果
2021/04/21 Javascript