Vue实现新旧版B站轮播图 | 世外天堂

Vue实现新旧版B站轮播图

很喜欢bilibili上的轮播图造型,这次就来自己写一下B站上的轮播图吧。
首先,B站分旧版和新版,仔细观察会发现,轮播图用的明显不是一套代码,因为旧版轮播图,播到最后一张时就倒回去了,是的,因为每一张图都有过渡,必须遵循这个规则,而且不可能第一张跑在最后一张的后面,这样用户体验不是很好,但我们还是要来做一下这种轮播图。
本次采用Vue框架进行开发,当然原生js和JQuery也可以,但是Vue支持数据双向绑定,响应速度快,简单,更便于维护。

旧版轮播图

代码如下:

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
a{
text-decoration: none;
}
li{
list-style-type: none;
}
.carousel {
max-width: 440px;
height: 220px;
position: relative;
overflow: hidden;
border-radius: 4px;
float: left;
}
.carousel .pic {
height: 220px;
transition: 1s ease;
}
.carousel img {
width: 440px;
height: 220px;
}
.carousel .pic li {
float: left;
}
/* 标题过渡 */
.carousel .title li {
position: absolute;
bottom: 10px;
left: 10px;
transition: .5s;
}
.carousel .title a {
color: white;
}
.carousel .trig {
position: absolute;
right: 35px;
bottom: 10px;
}
.carousel .trig li {
float: left;
background-image: url(img/icons.png);
background-position: -855px -790px;
width: 18px;
height: 18px;
margin-left: 5px;
cursor: pointer;
}
.carousel .trig li:hover {
background-position: -919px -790px;
}
.on {
background-position: -855px -727px !important;
width: 18px;
height: 18px;
}
.show {
opacity: 1;
}
.hide {
opacity: 0;
}
</style>
</head>
<body>
<div class="carousel" @mouseover="mouseover" @mouseout="mouseout">
<ul class="pic" :style="style">
<li v-for="(item,index) in pics"><a :href="pics[index].url"><img :src="pics[index].src"></a></li>
</ul>

<ul class="title">
<li v-for="(item,index) in pics" :class="pics[index].on?'show':'hide'"><a :href="pics[index].url">{{pics[index].title}}</a></li>

</ul>

<ul class="trig">
<li v-for="(item,index) in pics" :class="{on:pics[index].on}" @click="toggle(index)"></li>
</ul>
</div>
<script>
var vm = new Vue({
el: '.carousel',
data: {
index: 0,
time: '',
pics: [{
url: '#',
src: 'img/1.jpg',
title: '标题1',
on: true
},
{
url: '#',
src: 'img/2.jpg',
title: '标题2',
on: false
},
{
url: '#',
src: 'img/3.jpg',
title: '标题3',
on: false
},
{
url: '#',
src: 'img/4.jpg',
title: '标题4',
on: false
},
{
url: '#',
src: 'img/5.jpg',
title: '标题5',
on: false
}
]
},
methods: {
toggle: function(index) {
this.index = index;
this.pics[index].on = true;
for (var i = 0; i < this.pics.length; i++) {
if (i !== index)
this.pics[i].on = false;
}
},
mouseover: function() {
clearInterval(this.time);
},
mouseout: function() {
var that = this;
this.time = setInterval(function() {
if (that.index == that.pics.length - 1) {
that.index = 0;
that.pics[0].on = true;
that.pics[that.pics.length - 1].on = false;
} else {
++that.index;
that.pics[that.index].on = true;
that.pics[that.index - 1].on = false;
}
}, 3000)
}
},
computed: {
style: function() {
return {
marginLeft: this.index * (-100) + '%',
width: this.pics.length * 100 + '%'
}
},
},
mounted: function() {
this.mouseout();
}
});
</script>
</body>
</html>

实现效果如图:
avatar
这种轮播图原理是一串图片通过浮动拼在一起,利用marginLeft的位移作为动画实现的,比较容易实现,缺点是中间隔几个图的时候点击感觉比较晃,而且不是无缝轮播。

新版轮播图

接下来我们就来实现一下无缝轮播图,也就是新版B站首页所用的。
代码如下:

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
* {
margin: 0;
padding: 0;
}

a {
text-decoration: none;
}

li {
list-style-type: none;
}

.carousel {
max-width: 440px;
height: 220px;
position: relative;
overflow: hidden;
border-radius: 4px;
float: left;
}

.carousel .pic {
width: 440px;
height: 220px;

}

.carousel img {
width: 440px;
height: 220px;
}

.carousel .pic li {
position: absolute;
top: 0;
left: 0;
/* 这里一定要改成绝对定位 */
}

/* 标题过渡 */
.carousel .title li {
position: absolute;
bottom: 10px;
left: 10px;
transition: .5s;
z-index: 999999;
}

.carousel .title a {
color: white;
}

.carousel .trig {
position: absolute;
right: 35px;
bottom: 10px;
z-index: 999999;
}

.carousel .trig li {
float: left;
background-image: url(img/icons.png);
background-position: -855px -790px;
width: 18px;
height: 18px;
margin-left: 5px;
cursor: pointer;
}

.carousel .trig li:hover {
background-position: -919px -790px;
}

.on {
background-position: -855px -727px !important;
width: 18px;
height: 18px;
}

.show {
opacity: 1;
}

.hide {
opacity: 0;
}
</style>
</head>
<body>
<div class="carousel" @mouseover="mouseover" @mouseout="mouseout">
<ul class="pic" ref="pic">
<li v-for="(item,index) in pics"><a :href="pics[index].url"><img :src="pics[index].src"></a></li>
</ul>
<ul class="title">
<li v-for="(item,index) in pics" :class="pics[index].on?'show':'hide'"><a :href="pics[index].url">{{pics[index].title}}</a></li>
</ul>

<ul class="trig">
<li v-for="(item,index) in pics" :class="{on:pics[index].on}" @click="toggle(index)"></li>
</ul>
</div>
<script>
var vm = new Vue({
el: '.carousel',
data: {
index: 0,
time: '',
zindex: 1,
pics: [{
url: '#',
src: 'img/1.jpg',
title: '标题1',
on: true
},
{
url: '#',
src: 'img/2.jpg',
title: '标题2',
on: false
},
{
url: '#',
src: 'img/3.jpg',
title: '标题3',
on: false
},
{
url: '#',
src: 'img/4.jpg',
title: '标题4',
on: false
},
{
url: '#',
src: 'img/5.jpg',
title: '标题5',
on: false
}
]
},
methods: {
toggle: function(i) {
this.index = i;
this.pics[i].on = true;
for (let j = 0; j < this.pics.length; j++) {
if (j !== i)
this.pics[j].on = false;
}
this.zindex++;
this.$refs.pic.children[i].style.zIndex = this.zindex;
this.$refs.pic.children[i].style.transform = 'translate3d(0px,0px,0px)';
this.$refs.pic.children[i].style.transition = 'all 0.25s ease 0s';
if (i >= 1) {
//当前轮播图前面的全部向左移
for (let j = 0; j < i; j++) {
this.$refs.pic.children[j].style.transition = 'all 0.55s ease 0s';
this.$refs.pic.children[j].style.transform = 'translate3d(-440px,0px,0px)';
}
} else {
//点击第一个按钮,此时第一张图在最后一张图左边
this.$refs.pic.children[this.pics.length - 1].style.transition = 'all 0.55s ease 0s';
this.$refs.pic.children[this.pics.length - 1].style.transform = 'translate3d(440px,0px,0px)';
}
if (i < this.pics.length - 1) {
//当前轮播图后面的全部向右移
for (let j = i + 1; j < this.pics.length; j++) {
this.$refs.pic.children[j].style.transition = 'all 0.55s ease 0s';
this.$refs.pic.children[j].style.transform = 'translate3d(440px,0px,0px)';
}
} else {
//点击最后一个按钮,此时第一张图在最后一张图左边
this.$refs.pic.children[0].style.transition = 'all 0.55s ease 0s';
this.$refs.pic.children[0].style.transform = 'translate3d(-440px,0px,0px)';
}
},
mouseover: function() {
clearInterval(this.time);
//鼠标移入,第一张图在最后一张图左边
if (this.index == this.pics.length - 1) {
this.$refs.pic.children[0].style.transform = 'translate3d(-440px,0px,0px)';
}
//鼠标移入,第一张图在最后一张图左边
else if(this.index ==0){
this.$refs.pic.children[this.pics.length - 1].style.transform = 'translate3d(440px,0px,0px)';
}
},
mouseout: function() {
var that = this;
var i = that.index;
//鼠标移出,第一张图在最后一张图右边
if (i == that.pics.length - 1) {
that.$refs.pic.children[0].style.transform = 'translate3d(440px,0px,0px)';
}
//自动轮播
this.time = setInterval(function() {
that.zindex++;
if (i == that.pics.length - 1) {
i = 0;
} else {
++i
}
that.index = i;//必须储存index的值,否则值恒为0
that.$refs.pic.children[i].style.zIndex = that.zindex;
that.$refs.pic.children[i].style.transform = 'translate3d(0px,0px,0px)';
that.$refs.pic.children[i].style.transition = 'all 0.25s ease 0s';
that.pics[i].on = true;
//向左移动
if (i > 0) {
that.$refs.pic.children[i - 1].style.transition = 'all 0.55s ease 0s';
that.$refs.pic.children[i - 1].style.transform = 'translate3d(-440px,0px,0px)';
that.pics[i - 1].on = false;

} else {
that.$refs.pic.children[that.pics.length - 1].style.transition = 'all 0.55s ease 0s';
that.$refs.pic.children[that.pics.length - 1].style.transform = 'translate3d(-440px,0px,0px)';
that.pics[that.pics.length - 1].on = false;
}
//向右移动
if (i < that.pics.length - 1) {
that.$refs.pic.children[i + 1].style.transition = 'none 0s ease 0s';
that.$refs.pic.children[i + 1].style.transform = 'translate3d(440px,0px,0px)';
} else {
that.$refs.pic.children[0].style.transition = 'none 0s ease 0s';
that.$refs.pic.children[0].style.transform = 'translate3d(440px,0px,0px)';
}
}, 3000)
}
},
mounted: function() {
this.mouseout();
//初始状态,即0++++
for (let j = 1; j < this.pics.length; j++) {
this.$refs.pic.children[j].style.transform = 'translate3d(440px,0px,0px)'
}
this.$refs.pic.children[0].style.zIndex = '1';
}
});
</script>
</body>
</html>

这种轮播图比起上一种要复杂的多,但是看起来非常舒服,甚至比swiper的轮播图还要高档,给人的感觉就好像是两张图在相互切换,其他图在怎么变换根本看不出来。
根据我仔细的观察,分析出了原理,主要是通过translate3d变换位移,还需要加上叠放次序zindex,不然顺序会乱,你会看到其中一张图在过渡的时候压到其他图,这里的过渡要分开加,而不是写在CSS中,不是无脑加,第一张往最后一张图后面移动时不能有过渡动画,重点还要考虑第一张图和最后一张图的位置关系,这里一个过渡0.25s和一个过渡0.55s并不是没有道理,如果都设为0.55s可能会看到其它图的过渡惨杂进来
实现效果如图:
avatar