轮播图的简单实现3D版
参考的例子为网易云音乐APP。
从网易云的例子上看,3D的轮播图有一定的层次感。
也就是左右两张图距离用户是比较远的,而中间这张图离用户是比较近的。
远近可以理解成css中的z-index
。
我们可以先把大体的HTML结构给写出来(这里以三张图为例子)。
1 2 3 4 5 6 7
| <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
。
1 2 3 4 5 6 7 8 9
| .carousel__item { position: absolute; width: 500px; height: 350px; top: 0; left: 0; transition: all .5s; }
|
整个.carousel
盒子明显不能为500x350
,不然无法显示出三张图片。
我们可以指定为500+200x2
宽度,也就是左右各腾出200px
来放对应的图片。
1 2 3 4 5 6 7
| .carousel { position: relative; width: 900px; height: 350px; margin: 30px auto; }
|
为了展示的清楚点,填充图片。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <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>
|
设置下图片的样式:
1 2 3 4 5
| .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
来模拟下一次的情况。
1 2 3
| <div style="display: flex;align-items: center;justify-content: center;"> <button id="btn">下一张</button> </div>
|
然后在button
的click
事件上写逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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的,如果我们有四张图片的话。
很不对劲好吧…
为什么会出现这种情况呢?
可以从动图里面看到右边其实每次都存在一张不动图片,导致动画很奇怪。
并且图片的层次存在问题,对于第二张图之后的图片,应该是离用户越来越远的。
所以我们需要先改进动画的逻辑,先不管层次的问题。
由于左右每次应该都是只有一张的,所以不能把全部的图片都放到左边或者右边。
那么应该放哪里呢?答案是可以放中间。
可以把不显示的图片都放到中间,对于下一张,每次从中间取出一张图放到右边,原来左边的图放到中间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 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", "0");
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", "0"); };
|
那么基本上逻辑就差不多了,放一个自己的demo。