我写的瀑布流实现方法 (2012-05-16)

最近做了个瀑布流效果实现瀑布流有几种实现方法,
主要工作量在前端,我选择了用JS计算定位的方法,
这样可以保证各兼容性,
但是缺点也很明显,
就是对于客户端性能消耗比较大.
我做了一个DEMO,查看演示
瀑布流布局-fzxa
大概原理是滚动条在滚动到指定位置时加载数据
JS计算最低的列,累加数据
这样能保证不会有很长的列出现
这种布局对于SEO优化不好,需要权衡使用

记录一下需要注意的几个问题
1. 需要获取图片高度,目前用PHP端实现的,我觉得还是用JS端实现比较好一些
2. 隐藏需要用visibility: visible;属性站位
3. 默认透明度为0,才能有淡入效果
4.settimeout前要执行cleartimeout,这样可以防止重复执行
5.外边需要用position: relative;,里面的div需要设置absolute实现定位

 

主要代码逻辑

/**
 *  @desc 瀑布流逻辑
 *  @author fzxa <nonb.cn>
 *  @create 2012/05/14
 */
var page = 1;
var isRequest = 0;
var Waterfall = function(param){
	
	this.id = typeof param.container == 'string' ? document.getElementById(param.container) : param.container;
	this.colWidth = param.colWidth;
	this.colCount = param.colCount || 4;
	this.cls = param.cls && param.cls != '' ? param.cls : 'list_p';
	//可是区域高度
	this.oHeight = document.documentElement.clientHeight;
	//滚动条高度
	this.scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
	this.init();
	this.onScrollImg();
}

Waterfall.prototype = {

	init:function(){
		var _this = this;
		var col = [],	//列高
			iArr = [],	//索引
			nodes = _this.getByClass(_this.cls,_this.id),
			len = nodes.length;
		for(var i = 0; i < _this.colCount; i++){
			col[i] = 0;
		}
		
		for(var i = 0; i < len; i++){
			nodes[i].h = nodes[i].offsetHeight + _this.getMar(nodes[i]);
			iArr[i] = i;
		}
		
		for(var i = 0; i < len; i++){
			
			var ming = _this.getMinCol(col);
			nodes[i].style.left = ming * _this.colWidth + "px";
			nodes[i].style.top = col[ming] + "px";
			col[ming] += nodes[i].h;
		}
		
		_this.id.style.height = _this.maxArr(col) + "px";
		
	},
	
	onScrollImg:function(){
		var _this = this;
		var getUrl = '/p/campus/ajaxphotolist';
		var st;
		window.onscroll = function(){
			var loadTop = document.getElementById('loading').offsetTop;
			_this.scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
			_this.oHeight = document.documentElement.clientHeight;

			if( loadTop < _this.oHeight + _this.scrollTop ){
				
				//如果没有数据了,则AJAX继续请求数据
				clearTimeout(st);
				st = setTimeout(function(){
				
					//如果未请求,则请求ajax
					if(isRequest == 0){
						//isRequest = 1;  //改变请求状态
						
						$('#wf-main').append(temp);
						setTimeout(function(){
							new Waterfall({
								"container"	:"wf-main",
								"colWidth"	:241,
								"colCount"	:4,
								"cls"		:"list_p"
							});
							$(".list_p").css('visibility','visible').animate({opacity:1});
						},500);
						/*
						 $.ajax({
								url:getUrl,
								type:'post',
								dataType:'html',
								data:{'page':page},
								success:function(data){
									//console.log(data);
									if(data == ''){
										$("#loading").html('没有了,请刷新本页查看最新照片... ^_^');
									}
									isRequest = 0;
									page +=1;
									$('#wf-main').append(data);
									setTimeout(function(){
										new Waterfall({
											"container"	:"wf-main",
											"colWidth"	:241,
											"colCount"	:4,
											"cls"		:"list_p"
										});
										$(".list_p").css('visibility','visible').animate({opacity:1});
									},500);
								},
								error:function(err){
									isRequest = 0;
								}
						});
					*/
					}
				},500);
			} // end if							
		}
	},
	
	getByClass:function(cls,p){
		var arr = [],
			reg = new RegExp("(^|\s+)" + cls + "(\s+|$)","g"),
			nodes = p.getElementsByTagName("*"),
			len = nodes.length;
		for(var i = 0; i < len; i++){
			if(reg.test(nodes[i].className)){
				arr.push(nodes[i]);
				reg.lastIndex = 0;
			}
		}
		return arr;
	},
	
	getMar:function(node){
		var dis = 0;
		if(node.currentStyle){
			dis = parseInt(node.currentStyle.marginBottom);
		}else if(document.defaultView){
			dis = parseInt(document.defaultView.getComputedStyle(node,null).marginBottom);
		}
		return dis;
	},
	
	maxArr:function(arr){
		var len = arr.length,temp = arr[0];
		for(var ii= 1; ii < len; ii++){
			if(temp < arr[ii]){
				temp = arr[ii];
			}
		}
		return temp;
	},
	
	getMinCol:function(arr){
		var ca = arr,cl = ca.length,temp = ca[0],minc = 0;
		for(var ci = 0; ci < cl; ci++){
			if(temp > ca[ci]){
				temp = ca[ci];
				minc = ci;
			}
		}
		return minc;
	}
};

调用方法,放在onload事件里

        window.onload =  function(){
		new Waterfall({
			"container"	:"wf-main",
			"colWidth"	:241,
			"colCount"	:4,
			"cls"		:"list_p"
		});
	}
			

后端就负责发送数据,我还计算了图片的高度, 用YII实现的也贴一下吧

/**
 * ajax载入图片列表
 * @author fzxa
 * @create 2012/05/14
 */
public function actionAjaxPhotoList(){
	if (Yii::app()->request->isAjaxRequest){
	
		$page = (int)Yii::app()->request->getParam('page');
		
		$location = '全国';
		$gender = null;
		$data = SchoolsImage::get_images($location, $gender, $page);
		
		$html = '';
		foreach($data as $val){
			$height = getimagesize(UpFile::gIU($val['image'],'b'));
			$html .= '<div class="list_p" style="visibility:hidden; opacity:0;">';
			$html .= '<div class="photo"><a href="/p/campus/photo?userid=8317078&vid=1306"><img src="'.UpFile::gIU($val['image'], 'b').'" '.$height[3].'/></a></div>';
			$html .= '<div class="infor_photo"><!--下载次数:<span>--></span>  评论数:<span>'.$val['comment_counts'].'</span>  浏览数:<span>'.$val['viewNum'].'</span></div>';
			$html .= '<div class="down_l"><a href="http://hr.baijob.com/index.jsp?urlStr=%2Fservlet%2FcompanyResume%3Faction%3DoutSearch%26keyword%3D4919375">下载简历</a></div>';
			$html .= '</div><!--end list_p-->';
		}

		echo $html;
	}
	Yii::app()->end();
}

点击查看原文阅读(31) | 评论(1) | 分类:前端开发HTML5/JS

PHP长文章分页解决办法 (2012-05-09)

最近要做个长文章分页
看似简单的功能
实现起来确实比较有难度
网上搜索的都不太靠谱
问了一圈,没人会
长文章分页一个比较头疼的问题就是
就把HTML标签截断,导致文章错位
最后自己解决了,分享一下
以下为PHP解决办法:

php长文章分页

/**
 * 长文章分段
 * @param string $article 文章内容
 * @param number $word_number 文章字节限制
 * @return array
 */
private function article_addpage($article,$word_number=2000){

	$con = explode("<table",$article);
	if (count($con)>1){
		return $article; //如果包含了表格就跳过,不进行分页处理
	}else{
	$word_all="";
	$word_num=0;
	$n="";
	$weor_cha=($word_number/10);//(上下浮动每页字数的10%)
	$article=preg_replace("/<div(.*?)>/m", "<br>",  $article);
	$article= str_replace("</div>","",  $article);
	$article=preg_replace("/<span(.*?)>/m", "<br>",  $article);
	$article= str_replace("</span>","",  $article);
	$article=preg_replace("/<p(.*?)>/m", "<br>",  $article);
	$article= str_replace("</p>","",  $article);
	$article= str_replace(chr(10),"<br>",  $article);
	$article=preg_replace("/<(.*?)<br>(.*?)>/m", "<\1 \2>",  $article);

	$artinfo=split("<br>",$article);//根据字符串确定段落

	$art_num=count($artinfo);//确定段落数
	$page_num_word=array();

	for($i=0;$i<=$art_num-1;$i++){
		$page_num_word[$i]=strlen($artinfo[$i]);
		$word_num=$word_num+$page_num_word[$i];//得到字数

		if ($word_num-$weor_cha<=$word_number or $i==0){
		$word_all.=$artinfo[$i]."<br>";
		}else{

		$word_all.="[nextpage]<br>".$artinfo[$i];
		$word_num=0;
		}
	}

	return $word_all; 

	}
}


/**
 * 文章分页
 * @param string $article 文章内容
 * @param number $id 默认第一页
 * @param string $page 当前第几页
 * @return string
 */
private function article_fenpage($article,$id,$page){

	$artinfo=explode("[nextpage]",$article);//根据字符串确定段落
	$pages=count($artinfo);//确定段落数
	$tempurla="";
	$fenyedh="";
	// echo $pages;
	//=======================================分页导航
	//显示总页数
	if ($pages>1){

	//$fenyedh= $fenyedh."共有".$pages."页 ";
	$substart=$page-10;
	$sybend=$page+10;
	if ($substart<=1 ){
		$substart=1;
	}
	if ($sybend>=$pages ){
		$sybend=$pages;
	}
	//显示分页数
	$first=1;
	$prev=$page-1;
	$next=$page+1;
	$last=$pages;

	//$fenyedh = "<div class='pages c_txt'>";
	//$fenyedh= $fenyedh."<a href='?id=".$id."'>第一页</a> ";
	$fenyedh= $fenyedh."<a href='?page=".$id."' class='inline_b up_page'>上一页</a> ";
	for ($i=$substart;$i<$page;$i++){
	$fenyedh= $fenyedh."<a href='?page=".$i."' class='inline_b'>".$i."</a> ";
	}
	$fenyedh= $fenyedh.'<a href="javascript:void(0);" title="1" class="inline_b current_pages">'.$page.'</a> '; //当前页
	for ($i=$page+1;$i<=$sybend;$i++){
			$fenyedh= $fenyedh."<a href='?page=".$i."'  class='inline_b' title=".$i.">".$i."</a> ";}
	}
	if($next > $pages){
		$href = 'javascript:void(0);';
	}else{
		$href = '?page='.$next;
	}
	$fenyedh= $fenyedh."<a href='".$href."' class='inline_b down_page'>下一页</a> ";
	$fenyedh .='<a href="'.Yii::app()->request->hostInfo.'/'.Yii::app()->request->pathInfo.'?view=all" title="全文浏览" class="inline_b">全文浏览</a>';
	//$fenyedh= $fenyedh."<a href='?page=".$last."'>最后一页</a>";
	//$fenyedh= $fenyedh."</div>";

	//=======================================分页导航end
	return $fenyedh;
	//return $artinfo[$page-1]."<br>".$fenyedh; 
}

使用方法:

$page=!empty($_GET['page']) ? $_GET['page'] : 1;
$detailContent = $this->article_addpage($detail->content(),$this->cutesize); 
$cutepage= $this->article_fenpage($detailContent,1,$page);
$detailContent = explode('[nextpage]',$detailContent); 

点击查看原文阅读(39) | 评论(0) | 分类:PHP

BigPipe技术优化网站速度 (2012-05-06)

一些大网站都开始用BigPipe技术做优化了,
首先转一个新浪利用BigPipe技术的PPT

新浪微薄 BigPipe优化 http://blog.sina.com.cn/s/blog_482611850100xpb1.html


所谓BigPipe,指的是Facebook开发的用来改善客户端响应速度的技术。本质上讲,其实它并不是新事物,原理上等同于Yahoo在Best Practices for Speeding Up Your Web Site里提出的Flush the Buffer Early,不过BigPipe的实现更灵活,所以有必要了解一二。

我们平常浏览网页时的体验通常是串行的:浏览器发起请求,服务器收到后渲染页面,在此期间,浏览器除了等待别无选择,演示代码如下:

<?php
sleep(1);
$header = 'header';

sleep(1);
$content = 'content';

sleep(1);
$footer = 'footer';
?>
<html>
<head>
<title>test</title>
</head>
<body>

<div id="header"><?php echo $header; ?></div>

<div id="content"><?php echo $content; ?></div>

<div id="footer"><?php echo $footer; ?></div>

</body>
</html>
注:代码里用sleep模拟服务端耗时的操作。

如果我们把串行改成并行的方式呢?每当服务器生成新的内容立刻发送给浏览器,浏览器立刻渲染,不必等到接收到全部数据再处理,毫无疑问会提升用户体验,代码如下:

提醒一下,代码最后运行在Apache + Mod PHP环境,旧版本Apache可能需要关闭GZip。如果是Nginx + PHP FastCGI环境,因为fastcgi_buffers的存在,运行效果会非你所愿。

<html>
<head>
<title>test</title>
</head>
<body>

<?php sleep(1); ?>
<div id="header"><?php echo str_pad('header', 1024); ?></div>
<?php ob_flush(); flush(); ?>

<?php sleep(1); ?>
<div id="content"><?php echo str_pad('content', 1024); ?></div>
<?php ob_flush(); flush(); ?>

<?php sleep(1); ?>
<div id="footer"><?php echo str_pad('footer', 1024); ?></div>
<?php ob_flush(); flush(); ?>

</body>
</html>
注:某些浏览器必须接收到一定长度的内容才开始渲染,所以代码里用到了str_pad。

代码里用到ob_flush和flush把页面分块刷新缓存到浏览器,此时如果使用Firebug查看响应头的话,会发现:Transfer-Encoding=chunked,如此一来浏览器就可以分块渲染了。

BigPipe在此基础上更进一步,演示代码如下:

<html>
<head>
<title>test</title>
</head>
<body>

<div id="header"></div>

<div id="content"></div>

<div id="footer"></div>

<?php ob_flush(); flush(); ?>

<?php sleep(1); $header = str_pad('header', 1024); ?>
<script>
document.getElementById("header").innerHTML = "<?php echo $header; ?>";
</script>
<?php ob_flush(); flush(); ?>

<?php sleep(1); $content = str_pad('content', 1024); ?>
<script>
document.getElementById("content").innerHTML = "<?php echo $content; ?>";
</script>
<?php ob_flush(); flush(); ?>

<?php sleep(1); $footer = str_pad('footer', 1024); ?>
<script>
document.getElementById("footer").innerHTML = "<?php echo $footer; ?>";
</script>
<?php ob_flush(); flush(); ?>

</body>
</html>

使用BigPipe,先刷新布局(Layout),然后按块(header,content,footer)刷新相应的Javascript代码,从而实现页面内容的填充。

BigPipe之所以使用Javascript渲染页面,是因为这样一来渲染页面的时候,就不会被块的位置束缚住,如果我们的服务器支持多线程,那么就可以同时处理多块内容,哪块先处理好就把哪块刷新到浏览器,即便不支持多线程,服务器也可以按照内容的重要程度分主次先后渲染,不必拘泥于HTML代码的物理顺序。

此外还应注意一下BigPipe和Ajax二者的区别,对于一个分成若干个块的页面而言,如果使用Ajax的话,每一块都需要单独发送一个HTTP请求,而如果使用BigPipe的话,不管有多少块,都仅有一个HTTP请求。所以Ajax对服务器造成的压力会是BigPipe的若干倍。

注:BigPipe不利于SEO,应用时可通过User Agent判断请求是人还是搜索引擎,如果是人的话,则应用BigPipe渲染模式,如果是搜索引擎的话,则应用传统渲染模式。

点击查看原文阅读(38) | 评论(0) | 分类:前端开发HTML5/JS

世界花卉大观园 (2012-05-01)

 今年五一本来想去车展,但觉得人太多了,
哪也不想去,只想待在家里,
后来去了世界花卉大观园,比想象的大一点,
有点像植物园
想买点水,方圆几里连个小卖部都没有,挺郁闷
 

世界花卉大观园-1

世界花卉大观园-2

世界花卉大观园-3

世界花卉大观园-4

点击查看原文阅读(40) | 评论(0) | 分类:我的日记

我写的图片延迟加载类FzxaLazyLoad (2012-04-27)

图片延迟加载,现在比较流行, 俗称懒加载 
理论上会减少服务器压力,节省服务器资源
原理是当滚动条拉到屏幕可视位置时替换加载图标,显示真实图片
但这样对SEO不太好,需要根据需求权衡使用
我做了一个DEMO 请看演示 

下面是浏览器网络请求,从图中看到减少了很多请求

fzxa图片延迟加载类


代码比较简单,下面是FzxaLazyLoad类JS代码

/**
 * 图片延迟加载类
 * @author fzxa <nonb.cn>
 * @create 20120428
 */
var FzxaLazyLoad = function (elems,options,debug){
	
	//别名_this
	var _this = this;
	//Debug开关,默认关闭
	_this.debug = debug || false;
	//参数配置
	_this.options = _this._setOptions(options);
	//窗口高度
	_this.oHeight = document.documentElement.clientHeight;
	//滚动条高度
	_this.scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
	//监听对象
	_this._elems = document.getElementById(elems) || window;
	//需要Lzayload对象
	_this._elemsLazy = _this._elems.getElementsByTagName(_this.options.tagName);

	//初始化程序
    _this._initialize(_this);
	
}

FzxaLazyLoad.prototype = {
	
	/**
	 * LazyLoad 初始化执行
	 * @param {object} LazyLoad
	 */
	_initialize:function(_this){
		this.addEvent(window,'scroll',function(){
			var i = 0;
			var lazyCount = _this._elemsLazy.length;
			var lazyObj = _this._elemsLazy;

			for(i=0; i<lazyCount; i++){
				var positionObj = _getTops(lazyObj[i]);
				//重新获取scrollTop
				_this.scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
				if(positionObj < _this.oHeight+_this.scrollTop){
					var newSrc = lazyObj[i].getAttribute(_this.options.tagAttr);
					_show(lazyObj[i],newSrc);
					//Debug
					if(_this.debug == true){
						if(newSrc == null){
							var txt = "options.tagAttr参数错误.\n\n";
							alert(txt);
						}
					}
				}
			}
		});
		//@TODO 显示真实图片
		function _show(obj,newsrc){

			setTimeout(function(){
				obj.src = newsrc;
			},_this.options.time);
			
		}
		//@TODO 获取对象位置
		function _getTops(obj){
				var oTop=0;
				
				while(obj){
					oTop+=obj.offsetTop;
					obj=obj.offsetParent;
				}
				return oTop;
		}
	},
	
	/**
	 * LazyLoad 配置参数
	 * @param {object} options
	 * tagname : 需要lazy的标签
	 * tagAttr : lazy替换标签
	 */
	_setOptions:function(options){

		options.tagName = options.tagName || 'img';
		options.tagAttr = options.tagAttr || 'lazySrc';
		options.time 	= options.time || '1000';
		
		return options;
	},
	/**
	 * 绑定事件
	 * @param {object} el
	 * @param {string} event
	 * @param {function} fn
	 * @param {boolean} bCapture
	 */
	addEvent:function(el, event, fn, bCapture){
		var isCapture = bCapture ? bCapture : false;
		try {
			el.addEventListener(event, fn, isCapture);
		} catch (e) {
			try {
				el.attachEvent('on' + event, fn);
			} 
			catch (e) {
				el['on' + event] = fn;
			}
		}
	},
	/**
	 * 移除绑定事件
	 * @param {object} el
	 * @param {string} event
	 * @param {function} fn
	 */
	removeEvent:function( obj, type, fn ) { 
		 if (obj.removeEventListener) 
			 obj.removeEventListener( type, fn, false ); 
		 else if (obj.detachEvent) { 
			 obj.detachEvent( "on"+type, obj["e"+type+fn] ); 
			 obj["e"+type+fn] = null; 
		 } 
	}
}

fzxalazyload类库试用方法:

window.onload = window.onscroll = function(){
	var lazy = new FzxaLazyLoad('img',{
		'tagName':'img',  // 绑定标签
		'tagAttr':'_src', // 替换变迁
		'time' : '1000'   // 延迟加载时间
	});
}

点击查看原文阅读(104) | 评论(0) | 分类:前端开发HTML5/JS
<< 1 2 3 4 5 6 7 8 9 10 >>