我写的瀑布流实现方法 (2012-05-16)
最近做了个瀑布流效果实现瀑布流有几种实现方法,
主要工作量在前端,我选择了用JS计算定位的方法,
这样可以保证各兼容性,
但是缺点也很明显,
就是对于客户端性能消耗比较大.
我做了一个DEMO,查看演示
大概原理是滚动条在滚动到指定位置时加载数据
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();
}
PHP长文章分页解决办法 (2012-05-09)
最近要做个长文章分页
看似简单的功能
实现起来确实比较有难度
网上搜索的都不太靠谱
问了一圈,没人会
长文章分页一个比较头疼的问题就是
就把HTML标签截断,导致文章错位
最后自己解决了,分享一下
以下为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);
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渲染模式,如果是搜索引擎的话,则应用传统渲染模式。
世界花卉大观园 (2012-05-01)
今年五一本来想去车展,但觉得人太多了,
哪也不想去,只想待在家里,
后来去了世界花卉大观园,比想象的大一点,
有点像植物园
想买点水,方圆几里连个小卖部都没有,挺郁闷




我写的图片延迟加载类FzxaLazyLoad (2012-04-27)
图片延迟加载,现在比较流行, 俗称懒加载
理论上会减少服务器压力,节省服务器资源
原理是当滚动条拉到屏幕可视位置时替换加载图标,显示真实图片
但这样对SEO不太好,需要根据需求权衡使用
我做了一个DEMO 请看演示
下面是浏览器网络请求,从图中看到减少了很多请求

代码比较简单,下面是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' // 延迟加载时间
});
}



看不懂
