轮播图循环滚动实现

一、开始

  • 最近面试连续两次都问了轮播图第一张和最后一张怎么流畅滚动,虽然有思路,但是还有动手实现,今天早上动手试了试。

二、分析

  • 要循环滚动,自然会想到要通过控制位置去实现,一般思维是从头排到最后,但是这道题不应该这么想,应该把要显示的图片放在中间,然后再去放置前后的图片。
  • 首先要了解 inline-block 的特性,它是可以重叠的,并且后一位的位置会随着前一位的位置改变而改变。
    inline-block.gif
  • 所以我们只在在第二个图片之后每个都 margin-left 负的图片的宽度就能把全部图片都重叠在一起了
1
2
3
this.doms.forEach((item, idx) => {
item.style.marginLeft = (idx === 0 ? 0 : `-${this.width}px`);
})
  • 然后怎么分开呢?想到位置动画的优化,自然会想到 translate 这个属性,所以我们就根据他来实现图片分离,我们约定好 Math.floor(imgs.length/2)的这个位置是当前显示的图片
1
2
3
4
5
6
var base = Math.floor(this.doms.length/2);
var cen = base;
this.doms.forEach((item,idx) => {
item.style.visibility = (idx === cen ? 'visible' : 'hidden');
item.style.transform = `translateX(${this.width*(-(base--))}px)`;
})
  • 然后剩下的就没什么了,上一页和下一页都是改变数组的位置,然后调用上面那个方法就行了
  • 还有就是点击上一页和下一页是函数节流的问题了

三、完成

final.gif

  • 最后附上源代码
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<script type="text/javascript">
var carousel = document.getElementsByClassName('img-wrapper')[0];
var imgs = ['img/1.png', 'img/2.png', 'img/3.png', 'img/4.png', 'img/5.png', 'img/6.png', 'img/7.png', 'img/8.png', 'img/9.png'];
// 创建保存图片的dom
var doms = [];
imgs.forEach((item,idx) => {
var img = document.createElement('img');
img.src = item;
img.className = 'img';
doms.push(img);
})

var Gallery = function(doms, width, container){
this.doms = doms;
if(doms.length < 3){
throw new Error('please give me more than 2 pic')
}
this.width = width;
this.container = container;
this.base = Math.floor(this.doms.length/2);
this.init();
}
// 初始化
Gallery.prototype.init = function(){
var len = this.doms.length;
var frIdx, spLen;
// 如果长度为3
if(len === 3){
frIdx = 2;
spLen = 1;
}else{
frIdx = Math.ceil(len / 2);
spLen = len - frIdx;
}
// 重新排列
var front = this.doms.splice(frIdx, spLen);
this.doms = front.concat(this.doms);

// 插入
this.setStyle();
var base = frIdx - 1;
var frag = document.createDocumentFragment();
this.doms.forEach((item, idx) => {
item.style.marginLeft = (idx === 0 ? 0 : `-${this.width}px`);
frag.appendChild(item);
})
// 调整位置
this.container.appendChild(frag);
}
//上一页
Gallery.prototype.prev = function(){
this.reOrder('prev');
}
// 下一页
Gallery.prototype.next = function(){
this.reOrder('next');
}
// 改变modal层,重排
Gallery.prototype.reOrder = function(type){
if(type === 'prev'){
var newFirst = this.doms.pop();
this.doms.unshift(newFirst);
}else{
var newLast = this.doms.shift();
this.doms.push(newLast)
}
this.setStyle();
}
// 设置样式
Gallery.prototype.setStyle = function(){
var base = cen = this.base;
this.doms.forEach((item,idx) => {
item.style.visibility = (idx === cen ? 'visible' : 'hidden');
item.style.transform = `translateX(${this.width*(-(base--))}px)`;
})
}
// 点击节流
var throttle = function(fn, timeout){
var timer = null;
return function(){
if(timer){
return
}else{
fn();
timer = setTimeout(() => {
clearTimeout(timer);
timer = null;
}, timeout)
}
}
}
var s = new Gallery(doms, 440, carousel);

document.getElementById('prev').onclick = throttle(s.prev.bind(s), 600)
document.getElementById('next').onclick = throttle(s.next.bind(s), 600)

</script>