`
cloudgamer
  • 浏览: 61087 次
  • 性别: Icon_minigender_1
  • 来自: 顺德
社区版块
存档分类
最新评论

Lazyload 延迟加载(缓载)效果

阅读更多

Lazyload是通过延迟加载来实现按需加载,达到节省资源,加快浏览速度的目的。
网上也有不少类似的效果,这个Lazyload主要特点是:
支持使用window(窗口)或元素作为容器对象;
对静态(位置大小不变)元素做了大量的优化;
支持垂直、水平或同时两个方向的延迟。
由于内容比较多,下一篇再介绍图片延迟加载效果
兼容:ie6/7/8, firefox 3.5.5, opera 10.10, safari 4.0.4, chrome 3.0



 
程序效果预览和详细的程序分析


完整实例下载

 

 

程序说明

【基本原理】


首先要有一个_container容器对象,容器里面是_elems加载对象集合的元素。
停止或不加载集合里面的元素(隐藏或替换等方法)。
然后历遍集合元素,当元素在加载范围内,再进行加载。
加载范围一般是容器的视框范围,即浏览者的视觉范围内。
当容器滚动或大小改变时,再重新历遍元素判断。
如此重复,直到所有元素都加载后就完成。


【容器对象】

程序一开始先用_initContainer程序初始化容器对象。
先判断使用的是window(窗口)还是一般元素作为容器对象:
var container = this._container,
       isWindow = container == window || container == document
                     || !container.tagName || (/^(?:body|html)$/i).test( container.tagName );
如果是window,再根据文档渲染模式选择对应的文档对象:
if ( isWindow ) {
       var doc = document;
       container = doc.compatMode == 'CSS1Compat' ? doc.documentElement : doc.body;
}
定义好执行方法后,再绑定scroll和resize事件:
this._binder = isWindow ? window : container;

$$E.addEvent( this._binder, "scroll", this.delayLoad );
isWindow && $$E.addEvent( this._binder, "resize", this.delayResize );
如果是window作为容器,需要绑定到window对象上,为了方便移除用了_binder属性来保存绑定对象。


【加载数据】

当容器滚动或大小改变时,就会通过事件绑定(例如scroll/resize)自动执行_load加载程序。
ps:如果不能绑定事件(如resize),应手动设置执行。

当容器大小改变(resize)时,还需要先执行_getContainerRect程序获取视框范围。
要获取视框范围,一般元素可以通过_getRect方位参数获取程序来获取。
但如果容器是window就麻烦一点,测试以下代码:
<!doctype html>
<style>html,body{border:5px solid #06F;}</style>
<body>
<div style="border:1px solid #000;height:2000px;"></div>
</body>
</html>
<script>
alert(document.documentElement.offsetHeight)
</script>
在ie会得到想要的结果,但其他浏览器得到的是文档本身的高度。
所以在_getContainerRect程序中,其他浏览器要用innerWidth/innerHeight来获取:
this._getContainerRect = isWindow && ( "innerHeight" in window )
       ? function(){ return {
                     "left":       0, "right":       window.innerWidth,
                     "top":       0, "bottom":window.innerHeight
              }}
       : function(){ return oThis._getRect(container); };
ps:更多相关信息可以看“Finding the size of the browser window”。

在_load程序中,先根据位置参数、滚动值和阈值计算_range加载范围参数:
var rect = this._rect, scroll = this._getScroll(),
       left = scroll.left, top = scroll.top,
       threshold = Math.max( 0, this.threshold | 0 );

this._range = {
       top:       rect.top + top - threshold,
       bottom:       rect.bottom + top + threshold,
       left:       rect.left + left - threshold,
       right:       rect.right + left + threshold
}
在_getScroll获取scroll值程序中,如果是document时会通过$$D来获取,详细看这里dom部分
threshold阈值的作用是在视框范围的基础上增大加载范围,实现类似预加载的功能。
最后执行_loadData数据加载程序。


【加载模式】

程序初始化时会执行_initMode初始化模式设置程序。
根据mode的设置,选择加载模式:
switch ( this.options.mode.toLowerCase() ) {
       case "vertical" :
              this._initStatic( "vertical", "vertical" );
              break;
       case "horizontal" :
              this._initStatic( "horizontal", "horizontal" );
              break;
       case "cross" :
       case "cross-vertical" :
              this._initStatic( "cross", "vertical" );
              break;
       case "cross-horizontal" :
              this._initStatic( "cross", "horizontal" );
              break;
       case "dynamic" ://动态加载
       default :
              this._loadData = this._loadDynamic;
}
包括以下几种模式:
vertical:垂直方向加载模式
horizontal:水平方向加载模式
cross/cross-vertical:垂直正交方向加载模式
cross-horizontal:水平正交方向加载模式
dynamic:动态加载模式
其中前4种属于静态加载模式,适用于加载对象集合元素的位置(相对容器)或大小不会改变(包括加载后)的情况。
其中后两种又属于"cross"模式,适用于两个方向都需要判断的情况。
程序会对静态加载的情况尽可能做优化,所以应该优先选择静态加载模式。
而动态加载就没有以上约束,但也没有特别的优化。


【动态加载】

动态加载是使用_loadDynamic程序作为加载程序的:
this._elems = $$A.filter( this._elems, function( elem ) {
              return !this._insideRange( elem );
       }, this );
程序会用_insideRange程序来判断元素是否在加载范围内,并用filter筛选出加载范围外的元素,重新设置加载集合。

在_insideRange程序中,先用元素位置和加载范围参数作比较,判断出元素是否在加载范围内:
var range = this._range, rect = elem._rect || this._getRect(elem),
       insideH = rect.right >= range.left && rect.left <= range.right,
       insideV = rect.bottom >= range.top && rect.top <= range.bottom,
       inside = {
                     "horizontal":       insideH,
                     "vertical":              insideV,
                     "cross":              insideH && insideV
              }[ mode || "cross" ];

在动态加载中,不会为元素记录位置参数,所以每次都会用_getRect程序获取加载元素的位置信息。
动态加载会默认使用"cross"模式来判断,即水平和垂直方向都判断。
如果元素在加载范围内,会执行_onLoadData自定义加载程序,进行元素的加载。


【静态加载】

静态加载是程序的重点,也是程序的主要特色。
主要是利用集合元素位置大小固定的性质进行优化,用这个方式会大大提高程序执行效率,越多加载项会越明显。

原理是对加载集合进行排序,转换成有序集合,这样加载范围内的元素总是加载集合中连续的一段。
即可以把加载集合分成3部分,在加载范围前面的,在加载范围内的和加载范围后面的。
以horizontal模式左右滚动为例,加载过程大致如下:
1,记录每个元素的位置参数,按left坐标的大小对加载集合进行排序(从小到大),设置强制加载,跳到1.1;
1.1,记录加载范围,如果是强制加载,跳到1.2,否则跳到2;
1.2,设置索引为0,跳到3;
2,判断滚动的方向,如果向右滚动跳到3,否则跳到4,没有滚动的话取消执行;
3,向后历遍元素,判断元素是否在加载范围内,是的话跳到3.1,否则跳到3.2,如果没有元素,跳到6;
3.1,加载当前元素,并把它从集合中移除,跳回3;
3.2,判断元素的left是否大于容器的right,是的话跳到5,否则跳回3;
4,向前历遍元素,判断元素是否在加载范围内,是的话跳到4.1,否则跳到4.2,如果没有元素,跳到6;
4.1,加载当前元素,并把它从集合中移除,跳回4;
4.2,判断元素的right是否大于容器的left,是的话跳到5,否则跳回4;
5,当前元素已经超过了加载范围,不用继续历遍,跳到6;
6,合并未加载的元素,并记录当前索引,等待滚动,如果全部元素都加载了,就完成退出。
7,当容器滚动时,跳到1.1;当容器大小改变时,设置强制加载,跳到1.1;当容器位置发生变化时,需要重新修正元素坐标,跳到1;

首先加载元素会在_rect属性中记录位置参数,不用重复获取,是一个优化。
更关键的地方是每次滚动只需对上一次索引到加载范围内的元素进行判断,大大减少了判断次数。
大致理解了原理后,后面再详细分析。

在_initMode模式设置中,对静态加载的情况会调用_initStatic初始化静态加载程序。
并传递两个参数mode(模式)和direction(方向)。
根据方向判断方式分三种模式:"vertical"(垂直)、"horizontal"(水平)和"cross"(正交)。
这里先分析一下前两种模式。

在_initStatic程序中,先根据direction设置排序函数,再设置_setElems重置元素集合程序:
var pos = isVertical ? "top" : "left",
       sortFunction = function( x, y ) { return x._rect[ pos ] - y._rect[ pos ]; },
       getRect = function( elem ) { elem._rect = this._getRect(elem); return elem; };
this._setElems = function() {
       this._elems = $$A.map( this._elems, getRect, this ).sort( sortFunction );
};
其中_setElems有两个意义,一个是记录元素的坐标参数,还有是把加载集合用map转换成数组并排序。
因为自定义的加载集合有可以是NodeList,而用sort就必须先把它转换成数组。

最后设置_loadData加载函数:
this._loadData = $$F.bind( this._loadStatic, this,
       "_" + mode + "Direction",
       $$F.bind( this._outofRange, this, mode, "_" + direction + "BeforeRange" ),
       $$F.bind( this._outofRange, this, mode, "_" + direction + "AfterRange" ) );

其中_loadStatic静态加载程序是程序的核心部分,更关键的优化就在这里。
这里还给它包装了三个参数:
direction:方向获取的程序名;
beforeRange:判断是否超过加载范围前面的程序;
afterRange:判断是否超过加载范围后面的程序。
通过包装,除了方便参数的使用,还能使程序结构更加清晰。

direction可能是"_verticalDirection"(垂直滚动方向获取程序)或"_horizontalDirection"(水平滚动方向获取程序)。
在里面在调用_getDirection程序获取滚动方向:
var now = this._getScroll()[ scroll ], _scroll = this._lastScroll;
if ( force ) { _scroll[ scroll ] = now; this._index = 0; return 1; }
var old = _scroll[ scroll ]; _scroll[ scroll ] = now;
return now - old;
原理是通过_getScroll获取当前的滚动值跟上一次的滚动值_lastScroll相差的结果来判断。
如果结果是0,说明没有滚动,如果大于0,说明是向后滚动,否则就是向前滚动。
然后记录当前滚动值作为下一次的参考值。
如果是强制执行(force为true),就重置_index属性为0,并返回1,模拟初始向后滚动的情况。
强制执行适合在不能根据方向做优化的情况下使用,例如第一次加载、resize、刷新等。
虽然不能做优化,但保证了加载的准确性。

在_loadStatic中,先用direction获取方向值:
direction = this[ direction ]( force );
if ( !direction ) return;
没有滚动的话就直接返回。

然后根据方向和上一次的索引来历遍加载集合,其中关键的一点是判断元素是否超过加载范围。
这个主要是通过beforeRange和afterRange程序来判断的。
从_loadData的设置可以看出,它们是包装了对应compare判断程序参数的_outofRange程序。
在"_vertical"方向,compare可能是:
_verticalBeforeRange:垂直平方向上判断元素是否超过加载范围的上边;
_verticalAfterRange:垂直方向上判断元素是否超过加载范围的下边。
在"horizontal"方向,compare可能是:
_horizontalBeforeRange:水平方向上判断元素是否超过加载范围的左边;
_horizontalAfterRange:水平方向上判断元素是否超过加载范围的右边。
在_outofRange中,通过compare来判断是否超过范围:
if ( !this._insideRange( elem, mode ) ) {
       middle.push(elem);
       return this[ compare ]( elem._rect );
}
先用_insideRange判断元素是否在加载范围内,不是的话把元素保存到middle,再用compare判断是否超过加载范围。

回到_loadStatic程序,根据方向判断,如果是向后滚动,先根据索引,取出加载范围前面的元素,保存到begin:
begin = elems.slice( 0, i );
这一部分肯定在加载范围外,不需要再历遍,再向后历遍集合:
for ( var len = elems.length ; i < len; i++ ) {
       if ( afterRange( middle, elems[i] ) ) {
              end = elems.slice( i + 1 ); break;
       }
}
i = begin.length + middle.length - 1;
当afterRange判断超过加载范围后面,根据当前索引取出后面的元素,保存到end。
然后修正索引,给下一次使用。

如果是向前滚动,跟前面相反,根据索引取出加载范围后面的元素,保存到end:
end = elems.slice( i + 1 );
再向前历遍集合:
for ( ; i >= 0; i-- ) {
       if ( beforeRange( middle, elems[i] ) ) {
              begin = elems.slice( 0, i ); break;
       }
}
middle.reverse();
当beforeRange判断超过加载范围前面,根据当前索引取出前面的元素,保存到begin。
由于middle在beforeRange里面是用push添加的,但实际上是倒序历遍,所以要reverse一下。
ps:虽然push/reverse可以直接用unshift代替,但元素越多前者的效率会越高。

最后修正一下索引,合并begin、middle和end成为新的加载集合:
this._index = Math.max( 0, i );
this._elems = begin.concat( middle, end );
这样就完成了一次加载,等待下一次了。

这部分有点抽象,不太好表达,有什么疑问的地方欢迎提出。


【cross模式】

cross模式即正交方向加载模式,是指垂直和水平都需要判断的模式。
也就是说,元素需要同时在两个方向的加载范围内才会加载。
按主次方向又分两种模式:"cross-vertical"(垂直正交)和"cross-horizontal"(水平正交)。
前者以垂直方向为主,水平方向为次,后者相反。

在_initStatic程序中,如果使用cross模式,会设置_crossDirection滚动方向获取程序:
this._crossDirection = $$F.bind( this._getCrossDirection, this,
       isVertical ? "_verticalDirection" : "_horizontalDirection",
       isVertical ? "_horizontalDirection" : "_verticalDirection" );
可以看出,这是包装了primary和secondary参数的_getCrossDirection程序。
其中primary是主滚动方向获取程序,secondary是次滚动方向获取程序。
在_getCrossDirection中会根据主辅方向的滚动情况设置正交滚动值:
direction = this[ primary ]();
secondary = this[ secondary ]();
if ( !direction && !secondary ) {
       return 0;
} else if ( !direction ) {
       if ( this._direction ) {
              direction = -this._direction;
       } else {
              force = true;
       }
} else if ( secondary && direction * this._direction >= 0 ) {
       force = true;
}
包括以下几个情况:
1,主次方向都没有滚动的话,直接返回0;
2,主方向没有滚动而次方向有的话,就用上次滚动的反方向,如果没有上一次滚动就执行强制加载;
3,主次方向都有滚动,同时主滚动方向跟上次不是相反的话,就执行强制加载;
4,主次方向都有滚动,同时主滚动方向跟上次相反的话,按一般情况处理;
5,主方向有滚动而次方向没有的话,就是一般的情况,不用特别处理。

利用两个方向要同时判断的性质,在情况2只要从主方向加载范围内的元素找出在次方向也在加载范围内的就行了。
这个可以通过不断取反方向来实现。

情况3和情况4一般发生在刷新或设置了延迟时间比较长的情况。
如果主方向跟上次相同的话,可能会出现索引两边都有需要加载的元素的情况,不能确定方向,所以只能执行强制加载。
ps:如果在主方向的滚动量超过加载范围的话也能做优化,不过判断比较麻烦就不做了。
而如果方向相反的话,需要加载的元素只会出现在索引到加载范围的方向上,按一般情况历遍就行了。

cross模式跟其余两个静态加载模式的最主要区别就在于方向的判断上。
其他部分都差不多的,就不再详细说明了。


使用技巧

【选择模式】

如果加载元素位置固定大小不固定的情况下只能选择"dynamic"动态加载,否则应该优先选择静态加载。
在静态加载中,如果基本上是用于垂直或水平滚动,应该用"vertical"或"horizontal"模式。
两个方向都需要的话,如果主要是垂直滚动的话就用"cross-vertical"模式,否则用"cross-horizontal"模式。

【延迟html渲染】

Lazyload的一个作用就是延迟html渲染。
原理是先保存元素里面的html,当判断元素在加载范围里面时,再加载里面的html。
程序主要是做判断的部分,而如何保存和加载就看各位的想象力了。
以下几种方法个人认为还不错的:
1,ajax法:保存地址,加载时用ajax读取内容插入元素中;
使用恰当的话能有效节省服务器资源,特别是要读数据库的地方,但响应速度受网络影响。
2,textarea法:把html保存到一个textarea中,加载时把value插入元素中;
利用了textarea的特性,第二个实例就是用的这个方法,使用简单,响应速度快。
不过仅仅是html的话,貌似也没什么必要,可以考虑关联一些dom操作之类的。
2,注释法:把html保存到一个注释中,加载时把内容插入元素中;
跟textarea法类似,但效率应该更好,加载时找出nodeType为8的节点,再把nodeValue插入元素中;
但在ie如果用innerHTML添加注释会被自动忽略掉,使用时注意。

以上方法都有一个问题,在不支持js的时候不能平稳退化,谁有更好的方法的话欢迎赐教。
除此之外,还可以用来延迟js执行,css渲染等,下一篇还会有图片的延迟加载

【position的bug】

在写第一个实例的窗口模式时,遇到了两个bug:
在ie6/7,overflow为scroll或hidden的元素,其中position为absolute或relative的子孙元素会出现异常。
解决方法:
1.为包含块元素添加属性position:relative。
2.把该元素的position:relative属性去掉,使用默认的static定位,并通过margin-top等属性实现类似的效果。
参考自“IE6 CSS bug”。

还有一个问题是,在ie6,overflow为visible的元素,会被其内容撑开。
解决方法:
在ie6下,本来overflow为visible的元素设为hidden,并把内容position设为relative。
原理请看“IE6 overflow:visible bug”。

还要注意的是,加载元素只能是容器的子元素。


使用说明

实例化时,有必要参数元素集合,可以是元素数组或NodeList集合。

可选参数用来设置系统的默认属性,包括:
属性:    默认值//说明
container:       window,//容器
mode:              "dynamic",//模式
threshold:       0,//加载范围阈值
delay:              100,//延时时间
beforeLoad:       function(){},//加载前执行
onLoadData:       function(){}//显示加载数据

还提供了以下方法:
load:加载程序;
resize:容器大小改变加载程序,其参数说明是否重置元素集合;
delayLoad:延迟的load程序;
delayResize:延迟的resize程序;
isFinish:指明程序是否执行完成;
dispose:销毁程序,其参数说明是否加载所有元素。

 

程序源码

var LazyLoad = function(elems, options) {
	//初始化程序
	this._initialize(elems, options);
	//初始化container
	this._initContainer();
	//如果没有元素就退出
	if ( this.isFinish() ) return;
	//初始化模式设置
	this._initMode();
	//进行第一次触发
	this.resize(true);
};

LazyLoad.prototype = {
  //初始化程序
  _initialize: function(elems, options) {
	this._elems = elems;//加载元素集合
	this._rect = {};//容器位置参数对象
	this._range = {};//加载范围参数对象
	this._loadData = null;//加载程序
	this._timer = null;//定时器
	this._lock = false;//延时锁定
	//静态使用属性
	this._index = 0;//记录索引
	this._direction = 0;//记录方向
	this._lastScroll = { "left": 0, "top": 0 };//记录滚动值
	this._setElems = function(){};//重置元素集合程序
	
	var opt = this._setOptions(options);
	
	this.delay = opt.delay;
	this.threshold = opt.threshold;
	this.beforeLoad = opt.beforeLoad;
	
	this._onLoadData = opt.onLoadData;
  },
  //设置默认属性
  _setOptions: function(options) {
    this.options = {//默认值
		container:	window,//容器
		mode:		"dynamic",//模式
		threshold:	0,//加载范围阈值
		delay:		100,//延时时间
		beforeLoad:	function(){},//加载前执行
		onLoadData:	function(){}//显示加载数据
    };
    return $$.extend(this.options, options || {});
  },
  //初始化容器设置
  _initContainer: function() {
	var container = $$(this.options.container),
		isWindow = container == window || container == document
				|| !container.tagName || (/^(?:body|html)$/i).test( container.tagName );
	if ( isWindow ) {
		var doc = document;
		container = doc.compatMode == 'CSS1Compat' ? doc.documentElement : doc.body;
	}
	//定义执行方法
	var oThis = this, width = 0, height = 0;
	this.load = $$F.bind( this._load, this );
	this.resize = $$F.bind( this._resize, this );
	this.loadDelay = function() { oThis._delay( oThis.load ); };
	this.resizeDelay = function(){//防止重复触发bug
		var clientWidth = container.clientWidth,
			clientHeight = container.clientHeight;
		if( clientWidth != width || clientHeight != height ) {
			width = clientWidth; height = clientHeight;
			oThis._delay( oThis.resize );
		}
	};
	//记录绑定元素方便移除
	this._binder = isWindow ? window : container;
	//绑定事件
	$$E.addEvent( this._binder, "scroll", this.loadDelay );
	isWindow && $$E.addEvent( this._binder, "resize", this.resizeDelay );
	//获取容器位置参数函数
	this._getContainerRect = isWindow && ( "innerHeight" in window )
		? function(){ return {
				"left":	0, "right":	window.innerWidth,
				"top":	0, "bottom":window.innerHeight
			}}
		: function(){ return oThis._getRect(container); }	;
	//设置获取scroll值函数
	this._getScroll = isWindow
		? function() { return {
				"left": $$D.getScrollLeft(), "top": $$D.getScrollTop()
			}}
		: function() { return {
				"left": container.scrollLeft, "top": container.scrollTop
			}};
  },
  //初始化模式设置
  _initMode: function() {
	switch ( this.options.mode.toLowerCase() ) {
		case "vertical" ://垂直方向
			this._initStatic( "vertical", "vertical" );
			break;
		case "horizontal" ://水平方向
			this._initStatic( "horizontal", "horizontal" );
			break;
		case "cross" :
		case "cross-vertical" ://垂直正交方向
			this._initStatic( "cross", "vertical" );
			break;
		case "cross-horizontal" ://水平正交方向
			this._initStatic( "cross", "horizontal" );
			break;
		case "dynamic" ://动态加载
		default :
			this._loadData = this._loadDynamic;
	}
  },
  //初始化静态加载设置
  _initStatic: function(mode, direction) {
	//设置模式
	var isVertical = direction == "vertical";
	if ( mode == "cross" ) {
		this._crossDirection = $$F.bind( this._getCrossDirection, this,
			isVertical ? "_verticalDirection" : "_horizontalDirection",
			isVertical ? "_horizontalDirection" : "_verticalDirection" );
	}
	//设置元素
	var pos = isVertical ? "top" : "left",
		sortFunction = function( x, y ) { return x._rect[ pos ] - y._rect[ pos ]; },
		getRect = function( elem ) { elem._rect = this._getRect(elem); return elem; };
	this._setElems = function() {//转换数组并排序
		this._elems = $$A.map( this._elems, getRect, this ).sort( sortFunction );
	};
	//设置加载函数
	this._loadData = $$F.bind( this._loadStatic, this,
		"_" + mode + "Direction",
		$$F.bind( this._outofRange, this, mode, "_" + direction + "BeforeRange" ),
		$$F.bind( this._outofRange, this, mode, "_" + direction + "AfterRange" ) );
  },
  //延时程序
  _delay: function(run) {
	clearTimeout(this._timer);
	if ( this.isFinish() ) return;
	var oThis = this, delay = this.delay;
	if ( this._lock ) {//防止连续触发
		this._timer = setTimeout( function(){ oThis._delay(run); }, delay );
	} else {
		this._lock = true; run();
		setTimeout( function(){ oThis._lock = false; }, delay );
	}
  },
  //重置范围参数并加载数据
  _resize: function(change) {
	if ( this.isFinish() ) return;
	this._rect = this._getContainerRect();
	//位置改变的话需要重置元素位置
	if ( change ) { this._setElems(); }
	this._load(true);
  },
  //加载程序
  _load: function(force) {
	if ( this.isFinish() ) return;
	var rect = this._rect, scroll = this._getScroll(),
		left = scroll.left, top = scroll.top,
		threshold = Math.max( 0, this.threshold | 0 );
	//记录原始加载范围参数
	this._range = {
		top:	rect.top + top - threshold,
		bottom:	rect.bottom + top + threshold,
		left:	rect.left + left - threshold,
		right:	rect.right + left + threshold
	}
	//加载数据
	this.beforeLoad();
	this._loadData(force);
  },
  //动态加载程序
  _loadDynamic: function() {
	this._elems = $$A.filter( this._elems, function( elem ) {
			return !this._insideRange( elem );
		}, this );
  },
  //静态加载程序
  _loadStatic: function(direction, beforeRange, afterRange, force) {
	//获取方向
	direction = this[ direction ]( force );
	if ( !direction ) return;
	//根据方向历遍图片对象
	var elems = this._elems, i = this._index,
		begin = [], middle = [], end = [];
	if ( direction > 0 ) {//向后滚动
		begin = elems.slice( 0, i );
		for ( var len = elems.length ; i < len; i++ ) {
			if ( afterRange( middle, elems[i] ) ) {
				end = elems.slice( i + 1 ); break;
			}
		}
		i = begin.length + middle.length - 1;
	} else {//向前滚动
		end = elems.slice( i + 1 );
		for ( ; i >= 0; i-- ) {
			if ( beforeRange( middle, elems[i] ) ) {
				begin = elems.slice( 0, i ); break;
			}
		}
		middle.reverse();
	}
	this._index = Math.max( 0, i );
	this._elems = begin.concat( middle, end );
  },
  //垂直和水平滚动方向获取程序
  _verticalDirection: function(force) {
	  return this._getDirection( force, "top" );
  }, 
  _horizontalDirection: function(force) {
	  return this._getDirection( force, "left" );
  },
  //滚动方向获取程序
  _getDirection: function(force, scroll) {
	var now = this._getScroll()[ scroll ], _scroll = this._lastScroll;
	if ( force ) { _scroll[ scroll ] = now; this._index = 0; return 1; }
	var old = _scroll[ scroll ]; _scroll[ scroll ] = now;
	return now - old;
  },
  //cross滚动方向获取程序
  _getCrossDirection: function(primary, secondary, force) {
	var direction;
	if ( !force ) {
		direction = this[ primary ]();
		secondary = this[ secondary ]();
		if ( !direction && !secondary ) {//无滚动
			return 0;
		} else if ( !direction ) {//次方向滚动
			if ( this._direction ) {
				direction = -this._direction;//用上一次的相反方向
			} else {
				force = true;//没有记录过方向
			}
		} else if ( secondary && direction * this._direction >= 0 ) {
			force = true;//同时滚动并且方向跟上一次滚动相同
		}
	}
	if ( force ) {
		this._lastScroll = this._getScroll(); this._index = 0; direction = 1;
	}
	return ( this._direction = direction );
  },
  //判断是否加载范围内
  _insideRange: function(elem, mode) {
	var range = this._range, rect = elem._rect || this._getRect(elem),
		insideH = rect.right >= range.left && rect.left <= range.right,
		insideV = rect.bottom >= range.top && rect.top <= range.bottom,
		inside = {
				"horizontal":	insideH,
				"vertical":		insideV,
				"cross":		insideH && insideV
			}[ mode || "cross" ];
	//在加载范围内加载数据
	if ( inside ) { this._onLoadData(elem); }
	return inside;
  },
  //判断是否超过加载范围
  _outofRange: function(mode, compare, middle, elem) {
	if ( !this._insideRange( elem, mode ) ) {
		middle.push(elem);
		return this[ compare ]( elem._rect );
	}
  },
  _horizontalBeforeRange: function(rect) { return rect.right < this._range.left; },
  _horizontalAfterRange: function(rect) { return rect.left > this._range.right; },
  _verticalBeforeRange: function(rect) { return rect.bottom < this._range.top; },
  _verticalAfterRange: function(rect) { return rect.top > this._range.bottom; },
  //获取位置参数
  _getRect: function(node) {
	var n = node, left = 0, top = 0;
	while (n) { left += n.offsetLeft; top += n.offsetTop; n = n.offsetParent; };
	return {
		"left": left, "right": left + node.offsetWidth,
		"top": top, "bottom": top + node.offsetHeight
	};
  },
  //是否完成加载
  isFinish: function() {
	if ( !this._elems.length ) {
		this.dispose(); return true;
	} else {
		return false;
	}
  },
  //销毁程序
  dispose: function(load) {
	clearTimeout(this._timer);
	if ( this._elems || this._binder ) {
		//加载全部元素
		if ( load && this._elems ) {
			$$A.forEach( this._elems, this._onLoadData, this );
		}
		//清除关联
		$$E.removeEvent( this._binder, "scroll", this.loadDelay );
		$$E.removeEvent( this._binder, "resize", this.resizeDelay );
		this._elems = this._binder = null;
	}
  }
}

 

  • 大小: 50.8 KB
分享到:
评论
4 楼 cloudgamer 2010-03-08  
wangzhongjie 写道
相当不错,可以系统的看看这个方面的东西

谢谢支持
3 楼 cloudgamer 2010-02-26  
寄生虫 写道
还不错,淘宝也用的延迟加载


淘宝的内容延迟加载是用textarea那个方法
2 楼 wangzhongjie 2010-02-25  
相当不错,可以系统的看看这个方面的东西
1 楼 寄生虫 2010-02-24  
还不错,淘宝也用的延迟加载

相关推荐

    lazyload延迟加载

    Lazyload是通过延迟加载来实现按需加载,达到节省资源,加快浏览速度的目的。...由于内容比较多,下一篇再介绍图片延迟加载效果。 兼容:ie6/7/8, firefox 3.5.5, opera 10.10, safari 4.0.4, chrome 3.0

    图片延迟加载 lazyload

    然后引入 jQuery.lazyload 插件,如 `jquery.lazyload.min.js` 文件,提供延迟加载的功能。 2. **HTML 结构**:在 HTML 中,为需要延迟加载的图片添加特定的类名(通常是 `.lazyload`),同时设置 `data-src` 属性...

    Lazyload 延迟加载效果 -源码.zip

    "Lazyload 延迟加载效果"是一种优化网页性能的技术,它主要应用于图像和其他大体积资源的加载。在网页设计中,当用户滚动页面时,只加载可视区域内的内容,而将非可视区域的内容推迟到需要时再加载,这就是延迟加载...

    页面图片实现Lazyload 延迟加载效果

    首先,`LazyLoad.htm`是示例页面,它包含了一些延迟加载图片的HTML结构。这些图片通常会设置一个占位符,如一个小尺寸的灰色方块,并且`src`属性会被一个特殊的占位符URL(如"data-src")替代。例如: ```html 延迟...

    LazyLoad 延迟加载(按需加载)

    《延迟加载(LazyLoad):优化网页性能的关键技术》 在当今互联网时代,大型网站面临着一个挑战:如何在满足用户体验的同时,尽可能减少服务器资源的消耗。延迟加载(LazyLoad),也称为按需加载,就是为解决这一...

    网页内容延迟加载LazyLoad

    网页内容延迟加载(LazyLoad)是一种优化网页性能的技术,它主要应用于图片、视频或大量文本内容的加载。这种技术的核心思想是,只加载用户当前视口内的内容,当用户滚动页面,即将进入视口的内容才会被加载。这样...

    Lazyload图片延迟加载效果.rar

    描述"Lazyload图片延迟加载效果"进一步确认了这个压缩包的内容,即展示了如何实现在网页中应用懒加载技术,使得图片在用户需要看到它们时才被加载,而不是一次性全部加载。 标签"JS特效-图片相册"说明了这个实例...

    jquery.lazyload图片预加载效果 jquery预加载

    《jQuery LazyLoad 图片预加载技术详解》 在网页设计中,图片的加载速度往往直接影响着用户体验。当页面中包含大量图片时,如果一次性全部加载,可能会导致页面加载时间过长,用户需要等待,这无疑降低了网站的可用...

    图片延迟加载,lazyload.js使用实例

    在提供的压缩包文件“图片交换”中,可能包含用于演示延迟加载效果的图片资源。当你在Chrome浏览器的网络面板(开发者工具中)中查看加载情况时,可以看到只有当图片进入视口时,网络请求才会被触发,这正是`lazy...

    Lazyload图片延迟加载效果

    总结起来,"Lazyload图片延迟加载效果"是一个关键的前端性能优化技术,通过JavaScript的`Intersection Observer` API或滚动事件来实现,可以有效减少页面初始化时的加载时间,提高网页的响应速度和用户满意度。...

    懒加载LazyLoad延时加载图片效果特效.rar

    标题"懒加载LazyLoad延时加载图片效果特效.rar"表明这是一个关于使用懒加载技术实现图片延迟加载,并带有特效的实例。.rar文件通常包含压缩的资源,如HTML文件(LazyLoad.htm)和JavaScript文件(CJL.0.1.min.js),...

    jquery.lazyload 实现图片延迟加载

    **jQuery LazyLoad 插件详解:实现图片延迟加载** 在网页设计中,为了提升用户体验,减少页面初次加载的时间,一种常见的优化策略是采用图片延迟加载(Image Lazy Load)技术。`jQuery LazyLoad` 是一个非常实用的 ...

    LazyLoad类库 js 延迟加载工具

    **延迟加载(LazyLoad)技术概述** 延迟加载是一种优化网页性能的策略,它允许我们只在需要时才加载资源,如图片、JavaScript或CSS。这种技术显著减少了初始页面加载时间,提高了用户体验,尤其是在移动设备上,...

    jquery lazyload图片延迟加载技术

    **jQuery LazyLoad 图片延迟加载技术** 在网页设计中,图片是重要的元素之一,但大量图片的加载往往会导致页面加载速度变慢,用户体验下降。为了解决这个问题,开发者们引入了图片延迟加载(Lazy Load)技术。...

    jquery lazyload延时加载

    2. **自适应布局**:考虑到响应式设计,需要确保在不同设备和屏幕尺寸下,图片都能正确地延迟加载。 3. **图片尺寸优化**:对图片进行压缩和裁剪,减少图片大小,进一步节省流量。 4. **异步加载**:结合其他技术...

    JQuery Lazyload加载图片实例

    为了解决这一问题,jQuery 提供了一个插件——jQuery Lazyload,它允许我们实现图片的延迟加载(也称为懒加载),即图片只在用户滚动到可视区域时才开始加载。这种方式可以显著提升网页的加载速度,特别是对于拥有...

    lazyload.js图片延迟加载(懒加载)插件

    "lazyload.js图片延迟加载(懒加载)插件" 是一个JavaScript库,主要用于优化网页性能,特别是那些包含大量图片的页面。它实现了“懒加载”技术,即图片不会在页面加载时立即加载,而是在用户滚动到图片即将出现在...

    懒加载lazyload

    它的核心思想是延迟加载,即在用户滚动页面时,只加载可视区域内的元素,而不是一次性加载整个页面的所有资源。这极大地提高了网页的加载速度,减少了对用户设备带宽的消耗,提升了用户体验。 在懒加载技术中,`...

    js lazyload实现网页图片延迟加载特效

    在网页设计中,为了提高页面加载速度和优化用户体验,一种常见的技术是图片延迟加载,也称为懒加载(Lazy Load)。JavaScript 是实现这一功能的主要工具,它允许我们在用户滚动到图片时才加载图片,而不是一次性加载...

    lazyload.js实现图片异步延迟加载

    除了图片,`lazyload.js`还可以应用于其他元素的延迟加载,如iframe、video等,只需替换`data_attribute`即可。 综上所述,`lazyload.js`是一个强大的工具,可以帮助我们实现高效的图片异步延迟加载,提高网页性能...

Global site tag (gtag.js) - Google Analytics