-
Notifications
You must be signed in to change notification settings - Fork 80
Description
今天分享一个很常见的东西——图片轮播。
虽然说这是一个很常见的东西,但真要论起来,其实有很多地方可以优化。
比如淘宝首页的图片轮播,如果一个顾客的鼠标像我这样傻逼得晃过去晃过来,被他经过的轮播就“停止”了。
注意鼠标移动的轨迹和轮播的大图片,如果用户在轮播周期内反复_经过_轮播元素,图片轮播就会“永远”停止。而下方的图片已经经过了几次轮播了。
而同样的事情如果发生在心动游戏的官网上会,这不会有任何不符合预期的行为发生,鼠标悬停轮播停止,鼠标离开不影响轮播进程。
注意鼠标移动的轨迹和轮播的大图片
引题结束,希望能引起你的兴趣,如若不然你应该不会看到这行字。。。或者下行字。。。
这个是我12年刚进入心动时写的,需求很简单——图片轮播、右下角链接。
为了实现这个需求,代码也可以很简单:
- 定义$.fn.xdMarquee,写成jquery扩展
- 函数中下载图片,并插入DOM
- 定义start、next、clear来控制轮播
- 定义右下角指示器click事件
- 开始轮播
源代码:
https://github.com/ericdum/mujiang.info/blob/master/share/marquee.js
捡重要的代码看:
function marquee(data, options){
//设置、初始化....
//第二、三步
var the = this;
//第四步
this.controller.find('span').live('click', function(){
the.start(0, $(this).index());
});
//第五步
this.start();
}
function start( timeout, index ) {
this.clear(); //清除timeout
this.promise(timeout, index);
}
function next(index) {
// 执行动画 ....
this.current = index;
this.promise();//设置下次轮播
}
function promise( timeout, index ){
timeout = timeout>=0 ? timeout : this.timeout;
var the = this;
this.tm = window.setTimeout(function(){
the.next(index);
}, timeout);
}
function clear() {
window.clearTimeout(this.tm);
}悬停问题
因为我们认为用户鼠标悬停代表用户对图片产生了关注,所以悬停要暂停图片播放,就需要在上面初始化的方法中时候加入:
this.hovering = false;
$(this).hover(function(){
the.hovering = true;
}, function(){
the.hovering = false;
the.start();
});然后再next方法中加入:
if( this.hovering ) return;这就可以了(另外要做好点击切换时的处理)。完成以后便发现了文章一开头提到的问题。
为了解决这个问题,首先想到的是在鼠标移走之后让图片立即改变。代码很简单,把上面第二段代码hover中对start的调用改成the.start(500)就好了——鼠标离开后500毫秒变换图片。
//hover(function(){...},function(){...
the.start(500)但这就随之而来了另一个问题,如果用户老是经过这个地方,图片岂不是会不停地换——频率太快。
解决方案是如果图片播放的时间不足,则鼠标悬停对轮播不造成影响。实现的方法就是新增一个stopping状态。在next函数中判断hovering==true时,把它设置成true。而后在hover的第二个函数中判断:
$(this).hover(function(){
the.hovering = true;
}, function(){
the.hovering = false;
if( the.stopping ){
the.stopping = false;
the.start(500);
}
});
//in next function
if( this.hovering ) {
this.stopping = true;
return;
}性能问题
除了上面的工作以外还有个显而易见的问题:这种广告图片都是比较大的jpg图片(形状、色彩丰富),压缩的空间不大(jpg有损)。并且除了第一张,其他图片再3秒内是不需要显示的。所以决定分批下载,先下载第一张图,等第一张图下载完成之后再下载后续的图(这个时候这些图已经会排在下载队列的最后了)。
方案是扩展先前定义的initImages方法:
function initImages(){
var the = this;
this.marquee.empty();//保持卫生
this.appendImage(0, function(){
for(var i=1; i<the.data.length; i++){
the.appendImage(i);
}
});
}
function appendImage(index, callback){
//检查参数,准备DOM对象...
img.load(callback);
img.attr('src', data[0]);
//插入DOM...
}菊花问题
图片延迟加载和以及本身就是靠js加载的第一张图片,必然会有概率发生图片还未加载完就要被用户观看的问题。所以必然要有野菊花。
解决方案更简单了,直接css加到了root元素的背景上,图片载入后自然盖住他,完美无缝,切换每张图没载入都可以显示到菊花。
js方面修改appendImage方法,防止未加载的图片被用户看到(那个飘渺的白框)。
function appendImage(index, callback){
//...
img.hide();
img.load(function(){
$(this).show();
callback();
});
img.attr('src', data[0]);
//...
}其他问题
- 调整角标的显示逻辑,一次全部渲染,避免“跳”或乱序
- 调整轮播开始的逻辑,第一张图片加载完成后开始,保证广告的显示时间基本符合预期。
- 由于js放在页面底部,其实5张图片一起加载的性能差不多,兼容将第一张图片直接放在html中的情况。

