轮播图的简单实现position绝对定位版
轮播图的简单实现position绝对定位版。
在之前的帖子中尝试以HTML5中的flex布局来实现轮播图。
当然,实现有几个问题:
- 由于基于整个
box进行移动,比较难以实现单方向的无限下一张; - 由于基于整个
box进行移动,在切换某一张图时可能动画过快; - 由于基于整个
box进行移动,需要动态计算滑动的距离。
所以本篇实现以绝对定位以及上篇也使用到的translate3d来实现。
本篇实现参考了 B 站首页轮播图的实现(实现细节可能不一致,不过视觉上是一致的)。
使用的HTML结构和上一篇轮播图的HTML结构一样,如下:
1 | <div class="carousel"> |
本篇实现着重点为无限下一张的逻辑,对左右按钮以及下方小点的逻辑省略(最后会有总的实现代码)。
首先我们可以F12查看 B 站首页的轮播图的结构和样式切换。

从右侧的样式上可以看到,每个轮播框都是绝对定位的,然后通过样式来切换,样式主要是transform:translate3d(x, y, z)。
从图中几次style切换可以看出,初始定位都是全部放在右侧,当前第一张放在可视窗口内。
我们先编写下相应的css:
1 | .carousel { |
HTML如下:
1 | <div class="carousel"> |
效果如下:

现在如果进行一轮轮播,那么需要以下的步骤:
- 当前的轮播往中心往左移(离开可视窗口);
- 下一张轮播从右侧往中心移动(进入可视窗口)。
我们通过按钮来实现上面这个下一张的逻辑。
1 | const eles = document.getElementsByClassName("carousel__item"); |
效果如下:

可以看到效果基本出来了。
但是有个问题,上面的实现中没有对curIdx进行合法性判断(也就是要小于轮播数量的长度)。
如果一直下一张那么会报错,如下:

所以要在最后一张时,重新回到第一张。
1 | const eles = document.getElementsByClassName("carousel__item"); |
逻辑上应该没问题,但是实际是有问题的,如下:

发现第一轮没问题,但是第二次循环到 A 图的时候,就发现它从左边往中间移动了。
原因就是当前轮播图离开可视窗口之后,它就一直在左侧不可见区域了,而不是在右侧不可见区域了,如下:

所以需要补充的逻辑就是:在当前轮播图离开可视窗口之前,如果左侧不可见区域还有轮播图,那么把它放到右侧不可见区域。
我们可以用一个变量来保存左侧不可见区域轮播图的索引。
1 | let eles = document.getElementsByClassName("carousel__item"); |

看起来真不错~
但是,还是有问题…
回到我们的轮播逻辑,每次操作需要改变三张图片的样式:
- 原来就在左侧的,重新放到右侧;
- 当前位于可见区域的,离开进入左侧不可见区域;
- 当前位于右侧不可见区域的,进入可见区域。
所以轮播的数量必须不小于3张,如果是2张,那么上面的逻辑就不成立,如下:

如果是1张,那么轮播图直接不会动了,如下:

当然,一张图不轮播也没啥大问题,但是2张的时候应该是B后面跟着A(从右侧出来)。
对于每次操作,可以看作是一组绑定的操作,也就是每次必须控制三个节点(三个节点不同)。比较复杂,很容易写漏逻辑。
回归初心,我们要的效果就是下一张从右进入可视窗口,可视窗口的这张往左离开。
那么是否可以每次就控制两个节点来实现这个效果呢?
答案是可以的,之前需要控制三个节点的原因是需要提前把对应的节点搬到右侧(无动画),然后在本次渲染时进行动画过渡。
这得结合浏览器的渲染原理,浏览器会在两个宏任务之间渲染页面,所以我们完全可以先把节点先挪到对应位置,然后在下个宏任务中进行动画操作,如下:
1 | <div class="carousel"> |
PS:HTML 结构上换成图片展示了。
1 | const eles = document.getElementsByClassName("carousel__item"); |
效果如下:

这种情况下,对于一张图片的话:
- 如果不需要实现轮播,那么最简单,在下一张的逻辑前进行长度判断即可;
- 如果需要实现轮播,那么可以补一张重复的在它的后面,然后要处理小圆点的逻辑前进行长度判断即可。
效果如下(补一张的效果):

到现在,核心逻辑基本上就结束了,下面是一个完全实现的代码段,包括了左右按钮和小圆点逻辑。
HTML 结构简单,用 js 来生成结构。
1 | <div id="myCarousel"></div> |
css 结构和之前没啥不同。
1 | .carousel { |
js代码如下:
1 | function createCarousel(urls, config) { |
2 张以上的demo
js代码
1 | const carousel = createCarousel( |
效果如下:

1 张的demo
js代码
1 | const carousel = createCarousel(["../images/img1.jpg"], { |

后记
还有点不足就是没有实现定时器的部分,就交给看到帖子的你啦~
思路就是监听mouseenter和mouseleave来设置和清除定时器。
定时器就是右边按钮的逻辑。
在返回的销毁函数中销毁即可。
demo如下:
