介绍

使用jQuery完成的简单轮播图组件,可在同一页面多个组件同时轮播。轮播图的实现过程并不复杂,但在是在封装过程中,运用到较多基础知识点,有面向对象、prototype继承、原型链等等。

代码

html:

1
2
3
4
5
6
7
8
9
10
11
12
<div class="banner" id="J_bg_ban1">
  <ul>
    <li><a href="#"><img src="./img/banner_01.jpg" alt="广告图"/></a></li>
    <li><a href="#"><img src="./img/banner_02.jpg" alt="广告图"/></a></li>
    <li><a href="#"><img src="./img/banner_03.jpg" alt="广告图"/></a></li>
    <li><a href="#"><img src="./img/banner_04.jpg" alt="广告图"/></a></li>
    <li><a href="#"><img src="./img/banner_05.jpg" alt="广告图"/></a></li>
  </ul>
  <div class="indicator" id="J_bg_indicator1"></div>
  <div class="ban-btn clearfloat" id="J_bg_btn1">
    <a class="next-btn fr" href="javascript:">&gt;</a><a class="prev-btn fl" href="javascript:">&lt;</a>
  </div>

css:

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
.banner {
height: 325px;
width: 812px;
position: relative;
overflow: hidden;
}
.banner ul{
padding: 0;
margin: 0;
}
.banner ul li{
top: 0;
left: 0;
list-style: none;
position: absolute;
}
.banner ul li img{
height: 325px;
width: 812px;
display: block;
}
.ban-btn{
width: 100%;
position: absolute;
top: 136px;
z-index: 2;
}
.ban-btn a{
display: inline-block;
height: 60px;
width: 35px;
background: rgba(180,180,180,0.5);
font-size: 25px;
text-align: center;
line-height: 60px;
color: #fff;
text-decoration: none;
}
.next-btn{
float: right;
}
.prev-btn{
float: left;
}
.ban-btn a:hover{
background: rgba(100, 100, 100, 0.5);
}
.indicator{
width: 100%;
position: absolute;
text-align: center;
bottom: 15px;
z-index: 2;
}
.indicator a{
display: inline-block;
width: 20px;
height: 5px;
margin:0 3px;
background: #fff;
}
.indicator-active{
background: #FF8C00;
}

js:

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
function Carousel(bannnerBox, aBox, btnBox){
this.now = 0; //当前显示的图片索引
this.hasStarted = false//是否开始轮播
this.interval = null//定时器
this.liItems = null//要轮播的li元素集合
this.len = 0; //liItems的长度
this.aBox : null; //包含指示器的dom对象
this.bBox : null; //包含前后按钮的dom对象
/**
* 初始化及控制函数
* @param bannnerBox string 包含整个轮播图盒子的id或class
* @param aBox string 包含指示器的盒子的id或class
* @param btnBox string 包含前后按钮的盒子的id或class
*/
this.init = function() {
//初始化对象参数
var that = this;
this.liItems = $(bannnerBox).find('ul').find('li');
this.len = this.liItems.length;
this.aBox = $(bannnerBox).find(aBox);
this.bBox = $(bannnerBox).find(btnBox);
//让第一张图片显示,根据轮播图数量动态创建指示器,并让第一个指示器处于激活状态,隐藏前后按钮
this.liItems.first('li').css({'opacity': 1, 'z-index': 1}).siblings('li').css({'opacity': 0, 'z-index': 0});
var aDom = '';
for (var i = 0; i < this.len; i++){
aDom += '<a></a>';
}
$(aDom).appendTo(this.aBox);
this.aBox.find('a:first').addClass("indicator-active");
this.bBox.hide();
//鼠标移入banner图时,停止轮播并显示前后按钮,移出时开始轮播并隐藏前后按钮
$(bannnerBox).hover(function (){
that.stop();
that.bBox.fadeIn(200);
}, function (){
that.start();
that.bBox.fadeOut(200);
});
//鼠标移入指示器时,显示对应图片,移出时继续播放
this.aBox.find('a').hover(function (){
that.stop();
var out = that.aBox.find('a').filter('.indicator-active').index();
that.now = $(this).index();
if(out!=that.now) {
that.play(out, that.now)
}
}, function (){
that.start();
});
//点击左右按钮时显示上一张或下一张
$(btnBox).find('a:first').click(function(){that.next()});
$(btnBox).find('a:last').click(function(){that.prev()});
};
//前一张函数
this.prev = function (){
var out = this.now;
this.now = (--this.now + this.len) % this.len;
this.play(out, this.now);
};
//后一张函数
this.next = function (){
var out = this.now;
this.now = ++this.now % this.len;
this.play(out, this.now);
};
/**
* 播放函数
* @param out number 要消失的图片的索引值
* @param now number 接下来要轮播的图的索引值
*/
this.play = function (out, now){
this.liItems.eq(out).stop().animate({opacity:0,'z-index':0},500).end().eq(now).stop().animate({opacity:1,'z-index':1},500);
this.aBox.find('a').removeClass('indicator-active').eq(now).addClass('indicator-active');
};
//开始函数
this.start = function(){
if(!this.hasStarted) {
this.hasStarted = true;
var that = this;
this.interval = setInterval(function(){
that.next();
},2000);
}
};
//停止函数
this.stop = function (){
clearInterval(this.interval);
this.hasStarted = false;
}
// 初始化
this.init();
// 开始轮播
this.start();
};

调用时采用new操作符,如下:

1
2
var banner1 = new Carousel('#J_bg_ban1','#J_bg_indicator1','#J_bg_btn1');
var banner2 = new Carousel('#J_bg_ban2','#J_bg_indicator2','#J_bg_btn2');

存在问题

这里使用了构造函数来实现继承,上述代码虽然可以实现轮播需求,但是仔细分析发现,每次使用new操作符都会根据构造函数构建出造出一个新的实例对象,那就存在内部函数不能复用的缺点,这也是单独使用构造函数来自定义对象不可避免的。

在Carousel对象内的play方法、next方法、prev方法、strat方法、stop方法其实都是可以共用的,多个轮播件共用这些函数是完全没有问题的,而init初始化方法需要在每个实例上单独创建。单独使用构造函数创建对象,在使用new操作符创建新实例的时候,init初始化方法会被重新在每个实例上创建一遍,这正是我们想要的结果,而play方法、next方法、prev方法、start方法、stop方法这些可共用的方法也会在新实例上被重新创建,而创造多个完成一样任务的方法是完全没有必要的,所以需要将这些共有的方法提出来,让所有Carousel对象的实例都可以公用,这样就可以解决函数复用的问题。

改进

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
// 构造函数加原型形式
/**
* 构造函数初始化及开始轮播
* @param bannnerBox string 包含整个轮播图盒子的id或class
* @param aBox string 包含指示器的盒子的id或class
* @param btnBox string 包含前后按钮的盒子的id或class
*/
function Carousel(bannnerBox, aBox, btnBox) {
this.now = 0; //当前显示的图片索引
this.hasStarted = false; //是否开始轮播
this.interval = null; //定时器
this.liItems = null; //要轮播的li元素集合
this.len = 0; //liItems的长度
this.aBox = null; //包含指示器的dom对象
this.bBox = null; //包含前后按钮的dom对象
//初始化函数
this.init = function () {
//初始化对象参数
var that = this;
this.liItems = $(bannnerBox).find('ul').find('li');
this.len = this.liItems.length;
this.aBox = $(bannnerBox).find(aBox);
this.bBox = $(bannnerBox).find(btnBox);
//让第一张图片显示,根据轮播图数量动态创建指示器,并让第一个指示器处于激活状态,隐藏前后按钮
this.liItems.first('li').css({
'opacity': 1,
'z-index': 1
}).siblings('li').css({
'opacity': 0,
'z-index': 0
});
var aDom = '';
for (var i = 0; i < this.len; i++) {
aDom += '<a></a>';
}
$(aDom).appendTo(this.aBox);
this.aBox.find('a:first').addClass("indicator-active");
this.bBox.hide();
//鼠标移入banner图时,停止轮播并显示前后按钮,移出时开始轮播并隐藏前后按钮
$(bannnerBox).hover(function () {
that.stop();
that.bBox.fadeIn(200);
}, function () {
that.start();
that.bBox.fadeOut(200);
});
//鼠标移入指示器时,显示对应图片,移出时继续播放
this.aBox.find('a').hover(function () {
that.stop();
var out = that.aBox.find('a').filter('.indicator-active').index();
that.now = $(this).index();
if (out != that.now) {
that.play(out, that.now)
}
}, function () {
that.start();
});
//点击左右按钮时显示上一张或下一张
$(btnBox).find('a:first').click(function () {
that.next()
});
$(btnBox).find('a:last').click(function () {
that.prev()
});
}
//初始化
this.init();
//开始轮播
this.start();
}
/**
* 播放函数
* @param out number 要消失的图片的索引值
* @param now number 接下来要轮播的图的索引值
*/
Carousel.prototype.play = function (out, now) {
this.liItems.eq(out).stop().animate({
opacity: 0,
'z-index': 0
}, 500).end().eq(now).stop().animate({
opacity: 1,
'z-index': 1
}, 500);
this.aBox.find('a').removeClass('indicator-active').eq(now).addClass('indicator-active');
}
//前一张函数
Carousel.prototype.prev = function () {
var out = this.now;
this.now = (--this.now + this.len) % this.len;
this.play(out, this.now)
}
//后一张函数
Carousel.prototype.next = function () {
var out = this.now;
this.now = ++this.now % this.len;
this.play(out, this.now);
}
//开始函数
Carousel.prototype.start = function () {
if (!this.hasStarted) {
this.hasStarted = true;
var that = this;
this.interval = setInterval(function () {
that.next();
}, 2000);
}
}
//停止函数
Carousel.prototype.stop = function () {
clearInterval(this.interval);
this.hasStarted = false;
}
$(function () {
var banner1 = new Carousel('#J_bg_ban1', '#J_bg_indicator1', '#J_bg_btn1');
var banner2 = new Carousel('#J_bg_ban2', '#J_bg_indicator2', '#J_bg_btn2');
});

在这里对Carousel对象的原型对象进行了扩展,将play方法、next方法,perv方法,start方法和stop方法写进了Carousel的原型对象中,这样每次实例化的对象就可以共用这些方法。当然,实例化的时候也是使用new操作符。这样我们就可以在同一页面内多次使用这个轮播对象了。这种组合使用构造函数和原型的模式,是创建自定义类型最常用的方法,至此我们就完成了这个简单轮播对象的封装。

小结

本文是在我开始学习JavaScript继承时,对原型继承的一些理解和运用。文中所述的继承方式均是使用js特有的原型链方式,实际上有了ES6的类之后,实现继承的就变得十分简单了,所以这种写法现在也不在推荐使用了,对于对象的继承更推荐使用ES6的class来实现。