您的位置:首页 > 其它

给onScroll减减压

2015-05-29 18:06 351 查看
现在很多功能都需要监听onscroll事件完成的,然而onscroll触发得很频繁也是众所周知。

测试一下(请先让浏览器出现滚动条)

var no = 0;
/**
* 统计触发次数
*/
function statistical() {
console.log( ++no );
}
window.addEventListener('scroll', function() {
statistical();
});


减压方式

1.setTimeout、clearTimeout

var no = 0,
timeout;
/**
* 统计触发次数
*/
function statistical() {
console.log( ++no );
}
window.addEventListener('scroll',function() {
if ( timeout ) {
clearTimeout( timeout );
}
timeout = setTimeout(function() {
statistical();
timeout = null;
},300);
});


弊端

onScroll触发多少次,就会执行多少次clearTimeout、setTimeout,

一直滑动滑轮的时候,不会触发处理函数,因为onScroll一直在clearTimeout。

2.setInterval

var change = false,
no = 0;

/**
* 统计触发次数
*/
function statistical() {
console.log( ++no );
}
/**
* 间隔一定时间判断滚动坐标是否变化,变化则执行处理
*/
setInterval(function() {
if ( change ) {
change = false;
statistical();
}
},300);

/**
* 监听onScroll事件,告诉setInterval,滚动坐标已经发生改变
*/
window.addEventListener('scroll',function() {
change = true;
});


弊端

setInterval一直占用内存。

总结

setTimeout的方式clearTimeout、setTimeout太频繁,压力还是相当大;

而setInterval的方式,setInterval一直占用内存,但大多数的情况下,它只做判断,就是判断change是否为true。

onscroll真的只做一件事,就是设置change变量为true,压力少了很多。

由此可见,setInterval 优于 setTimeout。

但我们总不能一个处理,添加一个onScroll、一个setInterval呢,所以写成模块。

模块代码

!(function( obj ) {
//异步执行,有更好的方法,详见http://blog.csdn.net/zakkye/article/details/45890829
var asynFire = asynFire || function(handle) { setTimeout.call( window, handle ) };

//eventId前缀,是个随机字符串,避免一个页面存在两个 scrollListener 的时候,eventId相同
var idPf = Math.random().toString(36).substr(2).substr(0,4) + '_',
//累加Id
guid = 1,
//window 当前的scrollLeft
nX = 0,
//window 当前的scrollTop
nY = 0,
//setInterval Id
interval = 0,
//scrollListener 监听事件所用id
eventId = '.scrollListener',
//是否已初始化
inited = false,
//setInterval 延迟时间
lazy = 300,
//所有回调缓存对象
callbacks = {},
//滚动坐标是否已改变
change = false;

/**
* 初始化,对基本属性赋值及添加监听
*/
function init() {
//已经初始化就不操作
if ( inited ) return;
addEvent();
addInterval();
inited = true;
}

/**
* 添加scroll事件监听
*/
function addEvent() {
$(window).on('scroll' + eventId,function(e){
change = true;
});
}
/**
* 删除scroll事件监听
*/
function removeEvent () {
$(window).off(eventId);
}
/**
* 添加循环监听
*/
function addInterval () {
interval = setInterval(function(){
determine();
}, lazy);
}
/**
* 删除循环监听
*/
function removeInterval () {
clearInterval( interval );
interval = null;
}

/**
* 判断当时是否应该执行回调
*/
function determine() {
var $window,
_oX,
_oY,
_nX,
_nY;
if ( change ) {
change = false;
$window = $(window);
_oX = nX;
_oY = nY;
_nX = $window.scrollLeft();
_nY = $window.scrollTop();
scrollListener.fire( _nX, _nY , _nX > _oX, _nY > _oY );
nX = _nX;
nY = _nY;
}
}

/**
* 滚动监听功能(减少onscroll处理压力)
* @type {Object}
*/
var scrollListener = {
/**
* add 添加处理
* @param {Function} callback
* @return {boolean || number} 添加成功返回对应id,否则返回false
*/
add : function( callback ) {
var id,
$window;
if ( typeof callback === 'function' ) {
$window = $( window );
if ( !inited ) {
init();
}
id = idPf + ( guid++ );
callbacks[id] = callback;
//由于会先执行一次,所以必须先返回eventId,再执行,所以用了异步,而且事件监听本来也属于异步
//也存在一些函数,判断页面已经滚动到相应区域,执行一次处理后,就要scrollListener.remove了,所以必须异步
asynFire(function() {
callback({
x : $window.scrollLeft(),
y : $window.scrollTop(),
right : true,
down : true
})
});
return id;
}
return false;
},
/**
* 删除处理
* @param  {number} id 添加回调时返回的id
* @return {boolean}    是否删除成功
*/
remove : function( id ) {
if (id && callbacks[id]) {
delete callbacks[id];
return true;
}
return false;
},
/**
* 执行所有回调
* @param  {number} x    滚动条x坐标
* @param  {number} y    滚动条y坐标
* @param  {boolean} right 是否为向右滚动
* @param  {boolean} down 是否为向下滚动
* @return {scrollListener}
*/
fire : function( x, y, right, down ) {
var i,
data = {
x : typeof x === 'number' ? x : $(window).scrollLeft(),
y : typeof y === 'number' ? y : $(window).scrollTop(),
right : typeof right === 'boolean' ? right : true,
down : typeof down === 'boolean' ? down : true
};
for (i in callbacks) {
callbacks[i]( data );
}
return this;
},
/**
* 设置监听延迟时间
* @param {number} time 时间戳
* @return {scrollListener}
*/
setLazy : function( time ) {
if ( typeof time === 'number' ) {
//如果已经初始化,则马上修改监听时间
lazy = time;
if ( inited ) {
removeInterval();
addInterval();
}
}
return this;
},
/**
* 销毁
* @return {scrollListener}
*/
destroy : function () {
removeEvent()
removeInterval();
inited = false;
callbacks = {};
return this;
}
}

obj.scrollListener = scrollListener;
})( window );


测试例子

var id1 = scrollListener.add(function() {
console.log('scroll1');
});
var id2 = scrollListener.add(function() {
console.log('scroll2');
});
scrollListener.remove( id2 );
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  scroll