寒假到来又是读书的好季节,因此作了一本小册来补充知识效果以下: css
咱们须要将两张图合成一张有正反两面。这里须要将反面沿着 y 轴反转一下就能够正确的显示了。使用 transform:scale(-1,1)
本来左侧这张图是正面看时的视角,右侧图是反面看的视角 bash
想法dom
<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 是前后致使的用户看到的层级关系,因此反转的时候后面的图片就会被翻转到前面。动画
多页翻转时会遇到层级问题,仍是会出现只会显示最后一组图片,由于它的层级最高。 this
解决方式
首先要清楚右侧的第一张图片(即将翻页的那张图)必须显示在最上面,而左侧的图片(已翻转的图片)显示最后一张图片就达到咱们的想要的效果了。spa
// 经过 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>
}
复制代码
发现问题
当子项直接为图片是须要给图片设置 background
或 border
才能是 3d 改变层级的效果生效。而图标外层包一层div并不会出现该问题。我将div设置为 inline / inline-block 也不会出现该问题。目前还没搞清楚啥缘由?3d
上文中实现了📖翻页效果,试想下一本书若是有几百页,那么咱们须要建立一个页的dom,能不能尝试用最少的dom结构完成这些操做。 尝试用3页来模拟整本书的翻阅效果。
待解决的问题
transform:rotateY(deg)
来控制,经过控制整个角度来实现翻页效果方案
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>
}
复制代码
constructor(){
super()
this.state = {
// 全部页面列表
list:[],
// 实际展现页面的列表
displayList:[],
// 管理位置列表
positionList:['middle','right','right'],
}
}
复制代码
// 找到须要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 须要替换图片,由于至关于最开始的第一页又回到开始,咱们须要将第一页到数据更新就产生了新到页面。
经过手动实现翻页📖的效果,又能够开心的学习,对css了解更多一些。一开始想作这个效果思路有点乱,发如今开发前用文档记录下本身的思路,一个个解决进一步梳理比较有效果。下图是在实现过程当中梳理的想法,有助于本身一步步解决问题。