轮播图的简单实现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
如下: