都很忙,废话就不多说了,节省时间先上一个写好的轮播图
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
*{margin: 0;padding: 0;}
.ppt{width: 200px;height: 120px;border: 1px solid #ccc;
margin: 10px auto;overflow: hidden;position: relative;}
.ppt-box{width: 400%;height: 100%;transition-duration:0.5s;
transform: translate3d(0,0,0);}
.ppt-box div{width: 25%;height: 100%;float: left;}
.m{width:15%;height: 30%; position: absolute;top:0;bottom: 0;
background: rgba(0,0,0,0.5);margin: auto;cursor: pointer;}
.last{left: 0;}
.next{right: 0;}
.check-box{width: 100%;height: 20px;position: absolute;bottom: 4%;
text-align: center;}
.check-box span{height: 10px;width: 10px; border-radius: 100%;
background: #ccc;display: inline-block;margin: 0 5px;cursor: pointer;}
.check-box span.active{background: #999;}
</style>
</head>
<body>
<div class="ppt" id="slide1">
<div class="ppt-box">
<div class="slide">1</div>
<div class="slide">2</div>
<div class="slide">3</div>
<div class="slide">4</div>
</div>
<div class="m last">上</div>
<div class="m next">下</div>
<div class="check-box"></div>
</div>
</body>
<script src="zepto.min.js"></script>
<script>
var h = $('#slide1').height();
var w = $('#slide1').width();
var n = $('#slide1 .slide').size();
var s = 0;
var speed = 2000;
var timer;
var str = '';
for(let i=0;i<n;i++){
str += i==0?'<span class="active"></span>':'<span></span>';
}
$('#slide1 .check-box').html(str);
function move(){
timer = setTimeout(function(){
s++;
trans();
timer = setTimeout(arguments.callee,speed);
},speed);
}
function trans(){
if(s==n){
s=0;
}else if(s==-1){
s=n-1;
}
$('#slide1 .check-box span').removeClass('active').eq(s).addClass('active');
$('#slide1 .ppt-box').css('transform','translate3d(-'+s*w+'px,0,0)');
}
move();
$(#slide1').mouseover(function(){
clearTimeout(timer);
})
$('#slide1').mouseout(function(){
move();
})
$('#slide1 .last').click(function(){
s--;
trans();
});
$('#slide1 .next').click(function(){
s++;
trans();
})
$('#slide1 .check-box span').click(function(){
s = $(this).index();
trans();
});
</script>
</html>
效果图:

作为一个前端你别说你轮播图不会写,上面代码实现了一个几乎所有知名网站都用过的轮播图模式,没什么难度,我只说一下arguments.callee的callee这个属性是只有当函数执行时才定义,他代表正在执行的函数的函数实体,借此实现setInterval的效果,(setInterval在实际项目中的效果是不可靠的,原因请见《javascript高级程序设计》第三版609页)今儿要说的是面向对象,就不扯没用的,不懂搜一下。
操蛋,产品来了 :
“现在需求改了,页面不止这一个轮播图了,而是有3个完全一样的轮播图”
有人肯定想到用函数封装一下就OK了:
<script>
function slide(dom){
var h = $(dom).height();
var w = $(dom).width();
var n = $(dom + ' .slide').size();
//..........等等代码
$(dom + ' .check-box span').click(function(){
s = $(this).index();
trans();
});
}
slide('#slide1');
slide('#slide2');
slide('#slide3');
这种写法好处是避免了变量对全局环境的污染,也不用写三遍代码了,可是我们仔细看看就能发现,其实就跟把代码复制三遍一样,方法都是一样的,却占用了3次内存,只不过是看起来干净了,如果用不上前篇说的构造函数,原型,那不白扯了吗,那我们就看看怎么改造一下:
<script>
function Slide(dom){
this.dom = dom;
this.h = $(this.dom).height();
this.w = $(this.dom).width();
this.n = $(this.dom + ' .slide').size();
this.s = 0;
this.speed = 2000;
this.timer = null;
var str = '';
for(let i=0;i<this.n;i++){
str += i==0?'<span class="active"></span>':'<span></span>';
}
$(this.dom + ' .check-box').html(str);
$(this.dom).mouseover(()=>{clearTimeout(this.timer);})
$(this.dom).mouseout(()=>{this.move();})
$(this.dom + ' .last').click(()=>{
this.s--;
this.trans();
});
$(this.dom + ' .next').click(()=>{
this.s++;
this.trans();
})
$(this.dom + ' .check-box span').click(()=>{
this.s = $(this).index();
this.trans();
});
}
Slide.prototype = {
constructor : Slide,
move : function(){
var this_ = this;
this_.timer = setTimeout(function(){
this_.s++;
this_.trans();
this_.timer = setTimeout(arguments.callee,this_.speed);
},this_.speed);
},
trans : function(){
if(this.s==this.n){
this.s=0;
}else if(this.s==-1){
this.s=this.n-1;
}
$(this.dom + ' .check-box span').removeClass('active').eq(this.s).addClass('active');
$(this.dom + ' .ppt-box').css('transform','translate3d(-'+this.s*this.w+'px,0,0)');
}
}
var slide1 = new Slide('#slide1');
slide1.move();
var slide2 = new Slide('#slide2');
slide2.move();
var slide3 = new Slide('#slide3');
slide3.move();
</script>
写到这里,我先解释一下上段代码中大家可能不懂得地方:
- ()=>{ } 箭头函数,ES6的新函数,首先确定一点,箭头函数是没有名字的函数,那么也就是一个匿名函数,作用就是把this的指针指向调用此函数的函数的外部环境。
- 为啥有的变量用this.来代替(如:this.speed = 2000),有的变量用var声明(var str = ' ')。这是因为用this命名的数据是每个实例自己特有的数据,而且这些数据是需要后续代码操作的,所以要以this的形式保存起来,用var声明的变量说白了就是临时数据,就像一次性筷子,用完就扔了,没有存储的必要了,函数执行完就被回收掉了,我的《js变量》里已经说的很清楚了。
- 首先,Slide.prototype部分也可替换成:
Slide.prototype.move = function(){
//代码
}
Slide.prototype.trans = function(){
//代码
}
我们合并了一下,缺多写了一个constructor : Slide,上一篇我们已经说了,函数的原型上默认是有constructor这条属性的,存在的意义我也说了,我们重写了这个对象就要把这个对象补充上,方便我们识别此对象是由哪个对象构造出来的。(其实不写对于我们这个案例也没啥影响,但是我们要养成这个良好的习惯,毕竟我们要为以后构建大型JS框架做准备)
- var this_ = this; 在move这个方法中我们并没有用()=>箭头函数,而是用this_ = this这种老旧的办法,原因就是我们在定时器函数内用了arguments.callee这个属性,这个属性我们也说了,他是在函数执行时定义的一条属性,指向了函数本身,如果使用箭头函数,那么这个属性指向的就是执行函数外部环境而不是执行函数的本身了。
使用构造函数和原型的组合模式完美解决了我们的问题,但是,产品又死过来了:
“需求刚才变了,这三轮播图有的有控制键,有的没有,而且各轮播的速度和图片切换过渡的速度我们可能还要微调一下”。
呵呵呵,产品,我XXX。(XXX代表“我爱你”),有句老话说得好,行走江湖你可以年轻,但不能天真。我们这次来个比较全面的改造,来应付产品可能的一次又一次的进攻。
<body>
<div class="ppt" id="slide1">
<div class="ppt-box">
<div class="slide">1</div>
<div class="slide">2</div>
<div class="slide">3</div>
<div class="slide">4</div>
</div>
<div class="m last">上</div>
<div class="m next">下</div>
<div class="check-box"></div>
</div>
<div class="ppt" id="slide2">
<div class="ppt-box">
<div class="slide">1</div>
<div class="slide">2</div>
<div class="slide">3</div>
<div class="slide">4</div>
</div>
<div class="check-box"></div>
</div>
<div class="ppt" id="slide3">
<div class="ppt-box">
<div class="slide">1</div>
<div class="slide">2</div>
<div class="slide">3</div>
<div class="slide">4</div>
</div>
</div>
</body>
<script>
function Slide(dom,obj){
if(obj && typeof obj === 'object' && !(obj instanceof Array)){
Object.keys(obj).forEach(function(key){
this[key] = obj[key]
})
}
this.dom = dom;
this.h = $(this.dom).height();
this.w = $(this.dom).width();
this.n = $(this.dom + ' .slide').size();
this.s = 0;
this.timer = null;
$(this.dom + ' .ppt-box').css('transitionDuration',this.tranSpeed/1000 + 's');
if(this.mouseover){ //默认没有鼠标移入移出的事件,需要实力化时定义
$(this.dom).mouseover(()=>{clearTimeout(this.timer);})
$(this.dom).mouseout(()=>{this.move();})
}
if($(this.dom + ' .m').size()){ //如果有'.m'类元素才绑定事件
$(this.dom + ' .last').click(()=>{
this.s--;
this.trans();
});
$(this.dom + ' .next').click(()=>{
this.s++;
this.trans();
});
}
if($(this.dom + ' .check-box').size()){ //如果有'.check-box'类元素才穿件元素并绑定事件
var str = '';
for(let i=0;i<this.n;i++){
str += i==0?'<span class="active"></span>':'<span></span>';
}
$(this.dom + ' .check-box').html(str);
$(this.dom + ' .check-box span').click(()=>{
this.s = $(this).index();
this.trans();
});
}
}
Slide.prototype = {
constructor : Slide,
speed:2000, //默认的轮播图切换速度
tranSpeed:500, //默认的轮播图过渡速度
move : function(){
var this_ = this;
this_.timer = setTimeout(function(){
this_.s++;
this_.trans();
this_.timer = setTimeout(arguments.callee,this_.speed);
},this_.speed);
},
trans : function(){
if(this.s==this.n){
this.s=0;
}else if(this.s==-1){
this.s=this.n-1;
}
$(this.dom + ' .check-box span').removeClass('active').eq(this.s).addClass('active');
$(this.dom + ' .ppt-box').css('transform','translate3d(-'+this.s*this.w+'px,0,0)');
}
}
var slide1 = new Slide('#slide1',{
speed:4000, //设置了speed的值,代表不使用默认值
tranSpeed:700, //设置了tranSpeed的值
mouseover:true //使用鼠标移入移出事件
});
slide1.move();
var slide2 = new Slide('#slide2',{ //speed和tranSpeed全部使用默认值
mouseover:true //使用鼠标移入移出事件
});
slide2.move();
var slide3 = new Slide('#slide3'); //全部使用默认值,不使用鼠标移入移出事件
slide3.move();
</script>
效果图:

我们先来改变了什么:
1.Slide(dom,obj),多了一个obj参数,这个对象可以定义三个属性(可以定义多少个可用属性由我们自己制定标准)
{
speed : Number, //设置了speed的值,代表不使用默认值
tranSpeed : Number, //设置了tranSpeed的值
mouseover : Boolean //使用鼠标移入移出事件
}
这三个属性都是可选的,speed和tranSpeed之所以可选,是因为我们已经在Slide的原型对象上定义了这两条属性,
如果构造出的实例对象上有这两条属性,那么当有方法执行时,并且需要这两条属性参与运算,就直接使用实例对象的属性。
如果实例对象没有这两条属性,那么当有方法需要该两条属性时,查看实例对象没有该两条属性,就会顺着_ _ proto _ _向下一级,也就是此实例的构造函数的原型对象上查找,找到了,就拿来用,如果还找不到,就继续顺着 _ _ proto _ _向下一级查找,直到找到null。( _ _ proto _ _的最顶层指向一个null)
这就是原型链中的同名屏蔽规则
mouseover这条属性就不多说了,这个用在了构造函数内部判断,很容易理解。
- 构造函数内部多了几个if判断,根据html元素的有或者无来判断是否添加相关的事件函数。
注意:可能还会有人诧异为啥有的属性能放在原型对象上,有的不能呢?你为啥不把this.s = 0; this.timer = null;也放到原型对象上呢。说明下,原型上只能放所有构造出的实例通用的属性,拿this.s = 0来说,它代表的是轮播图当前展示图片的索引,如果把它放在原型上,所有轮播图都会按照这一个数值的索引显示图片,那不乱套了吗。
今天就到这了。有不懂得,或发现代码错误的,或有更简单的写法的,欢迎留言讨论
网友评论