轮播图的简单实现3D版
轮播图的简单实现3D版
参考的例子为网易云音乐APP。
从网易云的例子上看,3D的轮播图有一定的层次感。
也就是左右两张图距离用户是比较远的,而中间这张图离用户是比较近的。
远近可以理解成css中的z-index
。
我们可以先把大体的HTML结构给写出来(这里以三张图为例子)。
<div class="carousel">
<div class="carousel__item__container">
<div class="carousel__item"></div>
<div class="carousel__item"></div>
<div class="carousel__item"></div>
</div>
</div>
结构基本和之前的一样,不过样式可能会有所改变,为了使得可视窗口显示三张图片,需要对之前的样式进行修改。
之前对应每张图片的宽高不变,是500x350
。
.carousel__item {
position: absolute;
width: 500px;
height: 350px;
top: 0;
left: 0;
/* 设置过渡动画 */
transition: all .5s;
}
整个.carousel
盒子明显不能为500x350
,不然无法显示出三张图片。
我们可以指定为500+200x2
宽度,也就是左右各腾出200px
来放对应的图片。
.carousel {
position: relative;
width: 900px;
height: 350px;
/* 下面这个让整个盒子居中,方便看 */
margin: 30px auto;
}
为了展示的清楚点,填充图片。
<div class="carousel">
<div class="carousel__item__container">
<div class="carousel__item">
<img src="../images/img1.jpg" />
</div>
<div class="carousel__item">
<img src="../images/img2.jpg" />
</div>
<div class="carousel__item">
<img src="../images/img3.jpg" />
</div>
</div>
</div>
设置下图片的样式:
.carousel__item > img {
display: block;
width: 100%;
height: 100%;
}
现在的效果如下:
由于三张图片绝对定位都是top: 0; left: 0
,所以都在左上角,并且显示的是最后一张。
现在我们要先把图片挪到对应的位置。
对于第一张图,它应该是在中间的,所以添加transform: translate3d(200px, 0, 0)
。
位置是正确了,但是被最后一张图遮住了,给它添加个z-index
让它离屏幕"近一点"。
第二张图,它应该是在中间的右边的,因为当我们点击(往右的)下一张时,右边这张就会放到中间的位置。
给它加上transform: translate3d(400px, 0, 0)
。
到这里,大体的效果基本出来了,可以看到我们没有对第三张图做任何transform
变化。
很容易从逻辑上理解,它作为最后一张就应该在第一张(中间这张)的左边,这样当我们点击(往左的)上一张时,左边这张就会放到中间的位置。
为了更有层次感,我们把左右两张缩放,变小点,这里使用的是scale
。
感觉很对,接下来就是要实现滑动的逻辑了。
PS:本文只简单地实现往右下一张的逻辑。
由于3d轮播有一定的特殊性,所以这里不考虑三张图片一下的情况,也就是处理最少3
张的情况。
首先要分析当我们想要下一张时,需要如何调整对应图片的样式(只针对3
张的情况)。
第一张(中间这张)应该是往右边,原来右边的应该往中间,原来左边的应该往右边,也就是变成如下这样。
那么我们的逻辑就可以开始写了。
我们先添加一个button
来模拟下一次的情况。
<div style="display: flex;align-items: center;justify-content: center;">
<button id="btn">下一张</button>
</div>
然后在button
的click
事件上写逻辑。
const eles = document.getElementsByClassName("carousel__item");
let curIdx = 0;
let left = eles.length - 1;
let right = curIdx + 1;
const btn = document.getElementById("btn");
btn.onclick = function () {
// 中间的往左边
eles[curIdx].style.setProperty("transform", "translate3d(0,0,0) scale(0.8)");
eles[curIdx].style.setProperty("z-index", "1");
// 原来左边的放右间
eles[left].style.setProperty("transform", "translate3d(400px,0,0) scale(0.8)");
eles[left].style.setProperty("z-index", "1");
// 原来右边的往中间
eles[right].style.setProperty("transform", "translate3d(200px,0,0) scale(1)");
eles[right].style.setProperty("z-index", "3");
// 更新索引
left = curIdx;
curIdx = right;
right = (curIdx + 1) % eles.length;
};
然后我们就可以看到效果了:
看起来似乎不错,但是其实是有bug的,如果我们有四张图片的话。
很不对劲好吧…
为什么会出现这种情况呢?
可以从动图里面看到右边其实每次都存在一张不动图片,导致动画很奇怪。
并且图片的层次存在问题,对于第二张图之后的图片,应该是离用户越来越远的。
所以我们需要先改进动画的逻辑,先不管层次的问题。
由于左右每次应该都是只有一张的,所以不能把全部的图片都放到左边或者右边。
那么应该放哪里呢?答案是可以放中间。
可以把不显示的图片都放到中间,对于下一张,每次从中间取出一张图放到右边,原来左边的图放到中间
const eles = document.getElementsByClassName("carousel__item");
let curIdx = 0;
let left = eles.length - 1;
let right = curIdx + 1;
const btn = document.getElementById("btn");
btn.onclick = function () {
// 中间的往左边
eles[curIdx].style.setProperty("transform", "translate3d(0,0,0) scale(0.8)");
eles[curIdx].style.setProperty("z-index", "1");
// 原来左边的放中间
eles[left].style.setProperty("transform", "translate3d(200px,0,0) scale(0.8)");
eles[left].style.setProperty("z-index", "1");
// 原来右边的往中间
eles[right].style.setProperty("transform", "translate3d(200px,0,0) scale(1)");
eles[right].style.setProperty("z-index", "3");
// 更新索引
left = curIdx;
curIdx = right;
right = (curIdx + 1) % eles.length;
// 取现在对应的下一张,放到右边
eles[right].style.setProperty("transform", "translate3d(400px,0,0) scale(0.8)");
eles[right].style.setProperty("z-index", "1");
};
效果如下:
看起来没什么问题,但还是有一点小瑕疵,可能动图抽帧看不出来。
我们对除了中间的那张的z-index
设置为3
,其他都设置为1
,
当我们从第一张往右的时候,左边会闪一下(左边过渡到中间的一个动画),但这里应该是中间往左边的动画一直显示才对。
原因就是相同z-index
情况下,后写的节点比先写的节点离屏幕"更近"。
所以我们还需要解决下层次的问题。
- 中间这张看得见的往左边,
z-index
设置为1
; - 原来左边的这张
z-index
设置为0
,放到中间藏起来; - 原来右边的这张
z-index
设置为3
,也就是当前的展示的图片; - 从中间不可见的图片中取一张往右,
z-index
设置为0
。
const eles = document.getElementsByClassName("carousel__item");
let curIdx = 0;
let left = eles.length - 1;
let right = curIdx + 1;
const btn = document.getElementById("btn");
btn.onclick = function () {
// 中间的往左边
// 往左边为1
eles[curIdx].style.setProperty("transform", "translate3d(0,0,0) scale(0.8)");
eles[curIdx].style.setProperty("z-index", "1");
// 原来左边的放中间
// 中间的看不见的图片都是0
eles[left].style.setProperty("transform", "translate3d(200px,0,0) scale(0.8)");
eles[left].style.setProperty("z-index", "0");
// 原来右边的往中间
// 中间看的见的这张就是3
eles[right].style.setProperty("transform", "translate3d(200px,0,0) scale(1)");
eles[right].style.setProperty("z-index", "3");
// 更新索引
left = curIdx;
curIdx = right;
right = (curIdx + 1) % eles.length;
// 取现在对应的下一张,放到右边
// 中间不可见往右边为0
eles[right].style.setProperty("transform", "translate3d(400px,0,0) scale(0.8)");
eles[right].style.setProperty("z-index", "0");
};
那么基本上逻辑就差不多了,放一个自己的demo。