书本无限翻页动画

前言

寒假到来又是读书的好季节,所以做了一本小册来补充知识效果如下:

实现

制作一页

我们需要将两张图合成一张有正反两面。这里需要将反面沿着 y 轴反转一下就可以正确的显示了。使用 transform:scale(-1,1)原本左侧这张图是正面看时的视角,右侧图是反面看的视角

想法

  1. 将两张图合成一张时通过定位将图重叠在一起。
  2. 反转整张图时可以看到一页的两面
<div class="merge">
          <img src="https://static-zh.wxb.com.cn/karazhan/content/article/2020/1/16f8334cdef.jpg"/>
          <img src="https://static-zh.wxb.com.cn/karazhan/content/article/2020/1/16f82fce679.jpg"/>
</div>  

实际效果
发现旋转时无论转了多少角度都只能看见图二,因为图二的层级永远比图一高。

解决办法
使用 3d 视角来实现层级的改变
1.首先将父级设置为3d视角 2. 然后将图一的 Z 方向移动 1px ,z方向代表和用户的距离,本来图一图二的z方向是相同的,但是图二的层级高,所以看到的是图二,现在将图一向前移动1px,自然看到的是图一了。

.merge{
  position: relative;
  transform-style: preserve-3d ;
  transform-origin: left center;
}
img{
  width:200px;
  height: 300px;
  position: absolute;
  top:0;
  left: 0;
  background-color: #fff;
  &:nth-child(1){
    transform: translateZ(1px);
  }
}

为什么不使用 z-index
使用z-index 来改变层级是,由于两张图片还是在一个层级上,所以无论图片怎么反转,总有一张图片会始终覆盖另外一张图片。所以我们还得切换它们的z-index的层级,很麻烦。而 translateZ 是先后导致的用户看到的层级关系,所以反转的时候后面的图片就会被翻转到前面。

单页demo完整代码

制作多页

多页翻转时会遇到层级问题,还是会出现只会显示最后一组图片,因为它的层级最高。

解决方式
首先要清楚右侧的第一张图片(即将翻页的那张图)必须显示在最上面,而左侧的图片(已翻转的图片)显示最后一张图片就达到我们的想要的效果了。

  1. 多张页面的时候,一般我们使用遍历的方式,只需要把即将翻转的页面的 z-index 提到最高就实现了右侧的翻转的图片层级问题。而左侧的本身最后一张的层级会比之前高,所以我们不需要设置任何东西。
// 通过 isSelected 来控制层级问题
<div className="page-wrap">
        {
          list.map((item,index)=>{
            return <Merge {...item}="" isSelected="{selectedIndex===index}" rotateY="{selectedIndex<=index" ?0:rotateY}=""/>
          })
        }
      </div>

// merge 组件
render(){
    let {rotateY,left,right,isSelected} = this.props
    return <div className="merge" style="{{transform:`rotateY(${rotateY}deg)`,zIndex:isSelected?99:0}}"> 
          <img src="{left}"/>
          <img className="image" src="{right}"/>
     </div>   
  }

发现问题
当子项直接为图片是需要给图片设置 backgroundborder才能是 3d 改变层级的效果生效。而图标外层包一层div并不会出现该问题。我将div设置为 inline / inline-block 也不会出现该问题。目前还没搞清楚啥原因?

多页demo完整代码

进阶无限翻页效果

上文中实现了📖翻页效果,试想下一本书如果有几百页,那么我们需要创建一个页的dom,能不能尝试用最少的dom结构完成这些操作。 尝试用3页来模拟整本书的翻阅效果。

想法

  1. 通过下面这种图来分析翻书整个过程我把它分为 左中右三个部分

  2. 当中间页翻转到左侧时将左侧页回置到右侧,同时右侧翻转到中间页 这样实现了三张图片到循环翻转效果。

待解决的问题

  1. 翻转效果是通过 transform:rotateY(deg)来控制,通过控制整个角度来实现翻页效果
  2. 从中间件页翻转到左侧是需要过渡效果的,从左侧页翻转到右侧是不需要过渡效果
  3. 层级问题,将中间页到层级设置为三张最高的,之所以选择三张图片来模拟整本书的效果,也是因为三张图片在翻转的时候层级问题解决比较简单些。

方案

  1. 还是将每页抽离成组件基本代码如下
function Single (props){
    let {position,left,right} = props
    let rotateY = 0
    let zIndex = 0
    let duraction = 0
    // 从中间页翻转到左侧
    let isLeft = position === 'left'
    let isMiddle = position === 'middle'
    let isRight = position === 'right'
    if(isLeft){
        rotateY = 180
    }
    if(isMiddle){
        zIndex = 99
    }
    if(isLeft||isMiddle){
        duraction = 1
    }

    return <div className="merge" style="{{transform:`rotateY(${rotateY}deg)`,zIndex,transitionDuration:duraction" +'s'}}=""> 
          <img src="{left}"/>
          <img className="image" src="{right}"/>
     </div>   
}
  1. 控制书页的位置,初始化数据如下,left / middle / right 分别代表 左侧页面/中间页面/右侧页面
constructor(){
  super()
  this.state = {
    // 所有页面列表
    list:[],
    // 实际展示页面的列表
    displayList:[],
    // 管理位置列表
    positionList:['middle','right','right'],
  }
}
  1. 当我们点击下一页时,需要改变 positionList 中的位置的值 middle->left , left->right , right->middle
// 找到需要middle页的index
    let rightIndex = this.state.positionList.findIndex(item=>item==='right')
    // 找到 left 页的index
    let leftIndex = this.state.positionList.findIndex(item=>item==='left')
    // 找到 middle 页的index
    let middleIndex = this.state.positionList.findIndex(item=>item==='middle')
    let path = ''
    // 将left页进行翻转
    if(leftIndex!==-1){
      this.state.positionList[leftIndex] = 'right'
    }
    // 将中间一页翻转到left
    if(middleIndex!==-1){
      this.state.positionList[middleIndex] = 'left'
    }
    // rgiht 中第一张进行翻转
    this.state.positionList[rightIndex] = 'middle'

测试
按照我们预想的逻辑后测试发现,左侧那页还没等中间页翻转到,已经跑到右侧页面了。我们需要延迟左侧翻转到动作。
但是翻转是由 rotateY 来控制,是父级 props 传递过来的,子组件不能控制。想了一个办法就是在左侧的位置定位一张图片,该图片的地址和左侧图片的地址保持一致,每次点击下一页都动态修改。
这里我们只是相同的三张图片进行无限翻转,我们需要在翻转的时候加入新的图片。


加入新页面
为了便于分析用 0/1/2 分别代表 左侧/中间/右侧 页面。那么初始状态下我们设置的是 122 也就是 一张中间图两张设置为右侧的图片,来分析下何时需要加入新图
122 不需要替换图片 012 不需要替换图片 201 需要替换图片,因为相当于最开始的第一页又回到开始,我们需要将第一页到数据更新就产生了新到页面。

无限翻页完整demo

最后

通过手动实现翻页📖的效果,又可以开心的学习,对css了解更多一些。一开始想做这个效果思路有点乱,发现在开发前用文档记录下自己的思路,一个个解决进一步梳理比较有效果。下图是在实现过程中梳理的想法,有助于自己一步步解决问题。

关于动画效果如果有好的实现方式欢迎在评论区留下意见。

原文链接:juejin.im

上一篇:第一期前端早早聊 - 第五讲知识点总结
下一篇:结合el-tree和el-transfer搞一个树形穿梭框

相关推荐

  • 龙骨换装游戏系统设计与实现(基于Egret+DragonBones龙骨动画)

    如何开发一个可维护性,可扩展,跨平台的换装游戏呢,本文从产品设计、龙骨动画、前端再到后端如何管理等角度,来介绍骨骼换装游戏从0到1的实现过程。 项目背景   我们一直在思考,如何能激励学员自主学习的积...

    4 个月前
  • 零基础动画特辑之音量弹射🔊

    导语 动画是前端工作中很重要的一部分,有趣的交互动画实现常常能让用户刚到惊艳,兴奋,并更愿意使用你的产品。熟练掌握动画是前端工程师的必备技能之一。这篇文章将从零开始介绍如何实现一个音量发射的动画。

    1 个月前
  • 送给CSS初学者的悬停过渡动画三部曲

    CSS不一定要写得多么复杂才能实现特殊效果。如下就是三个超级简单的过渡的例子,可能只是几行代码,但是添加到Web应用程序中,却会让它增色不少。 图片描述(https://img.javascriptc...

    1 年前
  • 进入 CSS3 动画

    我最近有机会深入研究一些CSS3动画。 我使用了像animate.css这样的库,用javascript完成了动画,但从未做过任何自定义的CSS3工作 原文(https://codeguide.cn/...

    1 年前
  • 这样做动画交互,一点都不费力!

    本文由云社区发表 作者:paulzeng 导语:Lottie是Airbnb开源的一个面向 iOS、Android、React Native 的动画库,可实现非常复杂的动画,使用也及其简单...

    2 年前
  • 这样使用 GPU 渲染 CSS 动画

    大多数人知道现代网络浏览器使用GPU来渲染部分网页,特别是具有动画的部分。 例如,使用属性的CSS动画看起来比使用和属性的动画更平滑。 但是如果你问,“我如何从GPU获得平滑的动画?”在大多数情况下,...

    1 年前
  • 转行学前端的第 15 天 : 实现一个H5页面动画

    我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。 今日学习目标 按照昨天的规划,今天这边就需要从实例开始实战了,早上在稿定设计上找了一个模板,今天主要就是将这个h5 邀请函效...

    4 个月前
  • 跨浏览器JavaScript(不是jQuery…)滚动到顶部动画

    跨浏览器JavaScript(不是jQuery…)滚动到顶部动画...

    2 年前
  • 超级绚丽,16款前端动画特效,轰炸你的眼睛

    图片描述(https://img.javascriptcn.com/1975552521870f4069f960d66e30c114 "图片描述") 前言 HTML5一个相当出色的web技术,它不仅可...

    2 年前
  • 资源加载优化-让动画更加顺滑

    在平时web动画的开发中,会碰到一些资源加载的问题,比如动画所需要的资源还没加载完,动画就开始运行了,这样动画的体验就不是很好了。这篇文章就是专门来解决这个问题的。

    2 年前

官方社区

扫码加入 JavaScript 社区