简单瀑布流的实现(服务器不给出图片高度版)

2012年4月6日 2 条评论

额,这篇文章是从迅雷CUED博客转过来的,虽然那篇也是我写的,国际惯例给个链接吧……

先上Demo

瀑布流排序 : http://cued.xunlei.com/demos/publ/demo1.html

瀑布流+无限拖  http://cued.xunlei.com/demos/publ/demo2.html

瀑布流+无限拖 (js获取图片高度版)  http://cued.xunlei.com/demos/publ/demo3.html

随着pinterest的走红,瀑布流式的布局被越来越多的网站所使用,这种布局确实有很多好处,图片列表页有很强大的视觉感染力,而且还提高了用户“发现好图”的效率。瀑布流的实现有很多种方式,之前淘宝UED有篇文章详细的介绍过各种方式的优劣。今天我们主要讨论一下绝对排序来实现瀑布流的方式 即 Pinterest 采用的方式。

首先说下瀑布流的排序算法,参考demo1,思路非常简单,我们把瀑布流拆成三个部分来看:容器、列、格子

1.先计算当前屏幕最多能容纳几列瀑布,其值为 “向下取整(屏幕可见区域宽度/(格子宽度+间距))”;

2.为了保证容器的居中,将容器的宽度设置为 列数* (格子宽度+间距) – 间距,这里需要注意的是 当容器的宽度计算出来之后再显示,否则会造成页面宽度的抖动,影响体验。;

3.排序开始,先把前N(N为列数)个格子分别放到每一列中,然后每次寻找高度最小的一列,把格子放进去(left值为列序号*(格子宽度+间距),top值为 列高+间距),并刷新列的高度,遍历所有格子直到所有的格子都被排序。
最后将事件句柄绑定到window.onload和window.onresize上,一个瀑布流布局的页面就出来了。

 

 

这样的排序算法看起来很美好,可真正结合异步加载数据应用到页面里还要解决以下几个问题

1.当缩放浏览器窗口时会不断地触发事件,如果每次都响应的话会狂耗性能,需要在缩放动作结束后再执行重排方法。

2.页面滚动到底部请求数据成功之后只对新增的节点重排。

3.如果服务器无法给出图片高度,需要在图片加载完毕之后再进行重排。

 

第一个问题我是用setTimeout和clearTimeout来解决的,思路是窗口变化之后开始计时,如果窗口还在变换则从新开始计时,窗口不再变化则延时(很短的时间)触发重排事件。暂时只想到这个,这里应该还有更好的方法。
代码如下

var re;
window.onresize = function() {
    clearTimeout(re);
    re = setTimeout(resize,100);
};

 

第二个问题在于如果每次有新的数据加载,都要对整个容器内的节点进行重排,非常消耗性能。解决思路:

1.将列保存在全局数组中,每次重排或者新增格子之后更新数组的数据,这样下次执行排序算法的时候可以直接调用。
2.将新增格子保存在数组中作为参数传递给排序算法,仅对新格子进行遍历和操作。

第三个问题是如果服务器无法给出图片尺寸,那么必须在图片完全加载完毕之后才可进行排序(因为高度是实时获取的,图片不全高度有误差),这里没有什么好办法,只能遍历图片,每张图片加载成功后执行一个回调函数,将加载成功的图片数量+1,当加载成功的图片数量等于图片总数的时候执行排序方法。缺点是有一张图片加载不成功就无法看到所有的,真正项目中还是需要在异步加载数据的时候获取图片尺寸。

好了,以上就是在这次瀑布流实现过程中遇到的问题和解决方法,由一开始加载3-4屏就卡死到现在可以无限加载(ff,chrome),深感优化js的必要性和无限性。一点小心得写在这里权当抛砖引玉,大家对瀑布流实现的优化有什么见解欢迎一起交流讨论。

分类: Ajax 标签:

[无责任转载]世界上10个不为人知的历史真相

2012年3月15日 1 条评论
随着时光流逝,许多真相由于历史久远而变的扭曲,或者干脆淹没在历史的洪流中,被人们抛弃。有一些故事是伪造的,其目的不过是为了给历史人物增添 一些传奇色彩。然而,另一些故事由于与历史人物的联系太过紧密,有时候让人说不清真假。接下来让我们来看一看历史上被人误传的许多传奇故事……

  据《每日邮报》报道,意大利的考古专家团称,马可波罗根本没有到过亚洲,他到中国的旅行故事都是编造的,来源于他在黑海附近遇到的商人。报道 称,由于其亚洲之旅而而极富盛名的传奇探险家马可波罗不过是个骗子。马可波罗的故事让人们觉得仿佛是他的亲身经历一样。有时,即便是最为缜密的历史学家也 会失去对于真相的判断。

阅读全文…

分类: 论史 标签:

css+js实现li的float折行效果

2012年1月29日 6 条评论

废话不多说,先上demo: http://pad.xunlei.com/htc/detail.html?movie_id=58674 

大多视频网站的剧集播放页面都会有一个完整的剧集列表呈现在播放器的下面或者其他什么地方,类似:

像这样的列表可以使用ul>li左浮动来布局,里面的元素会按照1->n的顺序来排列。隐藏ul多出的部分,然后通过js来控制ul的定位来实现剧集的切换。
但是当列表为两行的时候  我们需要的顺序是

而用刚才li浮动的方法,我们所能得到的顺序是

1  2  3  4  5
6  7  8  9  10

那么我们在第一屏所看到的就是

1  2  3  4
6  7  8  9

翻页之后是
5
10

这显然不符合要求,我们先用css让列表从左至右递增:
html代码:

<style>
li{float:left;display:inline-block;width:100px;height:40px;background:#ff0000;margin:10px 10px 0 0;text-align:center;}
li:nth-child(2n){margin:60px 0 0 -110px;}
</style>
<ul>
    <li>第一集</li>
    <li>第二集</li>
    <li>第三集</li>
    <li>第四集</li>
    <li>第五集</li>
    <li>第六集</li>
    <li>第七集</li>
    <li>第八集</li>
</ul>

这样我们得到的顺序就是:

1  3  5  7  9
2  4  6  8  10

虽然还不是最终需要的顺序,但是起码由小到大的逻辑没问题了

最后用js改变一下dom的结构,假设每页显示8条数据,那么我们需要的dom结构就是1  5  2  6  3  7  4  8   9  13……(数字代表集数)

 

<ul id="u1">
    <li style="background:#ff0000;">第一集</li>
    <li style="background:#ff0000;">第二集</li>
    <li style="background:#ff0000;">第三集</li>
    <li style="background:#ff0000;">第四集</li>
    <li style="background:#ff0000;">第五集</li>
    <li style="background:#ff0000;">第六集</li>
    <li style="background:#ff0000;">第七集</li>
    <li style="background:#ff0000;">第八集</li>
    <li style="background:#ff0000;">第九集</li>
    <li style="background:#ff0000;">第十集</li>
</ul>
<style>
li{float:left;display:inline-block;width:100px;height:40px;margin:10px 10px 0 0;text-align:center;}
li:nth-child(2n){margin:60px 0 0 -110px;}
</style>
<script language="javascript">
    var arr=document.getElementsByTagName("li");
	var x = Math.round(arr.length/2) > 4?Math.round(arr.length/2):4;	//ul每行最大的li个数
	var x_b_t=0;
	var narr = new Array();//按照新顺序构建新数组
	for(var i=0; i <x+1 ;++i)
	{
	   if(i%4 == 0 && i>0)//翻页
	   {
		  x_b = x_b_t  = ++x_b_t+4;
		  push(narr,arr[x_b-1]);
	   }
	   else
	   {
		  x_b = ++x_b_t;
		  push(narr,arr[x_b-1]);
	   }
		var x_p = parseInt((x_b)+4);//构建第二行的元素
		if(x_p < (Math.floor(arr.length)/8+1)*8)
	   {
	      if(x_p<arr.length){push(narr,arr[x_p-1]);}
		 else{
			push(narr,document.createElement("li"));//插入空标签保证顺序
		 }
		}
	}
function push(a,x)//数组入栈
{
	var l=a.length;
	a[l]=x;
}
var b=document.getElementById("u1");
for (var k=0;k<narr.length;k++){
	b.appendChild(narr[k]);
}

</script>

这里只是用js改变已有的dom顺序,在实际的应用中可以用php等语言直接构建符合要求的dom结构,基本思路大体一致。到此为止我们就实现了每页有n集,并且分行显示,每页递增的视觉效果了。

ps:在不支持css高级选择器的浏览器下,可以用js来控制第偶数个li元素的margin属性,来达到折行的效果

ps2:算法原作者:huangwanfang  

同域及跨域下iframe高度自适应(兼容IE/FF/OP/Chrome)

2011年11月21日 6 条评论

采用JavaScript来控制iframe元素的高度是iframe高度自适应的关键,同时由于JavaScript对不同域名下权限的控制,引发出同域、跨域两种情况。

由于客户端js使用浏览器的同源安全策略,跨域情况下,被嵌套页面如果想要获取和修改父页面的DOM属性会出现权限不足的情况,提示错误:Permission denied to access property ‘document’。这是因为除了包含脚本的文档载入的主机外,同源策略禁止客户端脚本链接到其他任何主机或者访问其他任何主机的数据。这意味着访问一个web服务的javascript代码通常只有在它也驻留在Web服务本身所在的同一个服务器的时候才有用。

所以在跨域情况下,我们遇到的问题就是:父窗口无法获得被嵌套页面的高度,而且被嵌套页面也无法通过驻留在其服务器上的js修改父窗口Dom节点的属性。所以我们需要一个媒介,来获得被嵌套页面的高度同时又能修改主界面iframe节点的高度。

思路:现有主界面main在域a下,被嵌套页面B在域b下,被嵌套页面B又嵌套一个在域a下的中介页面A。 当用户打开浏览器访问mail.html的时候载入B,触发B的onload事件获取其自身高度,然后B载入A,并将高度值作为参数赋值给A的location对象。这样A就可以通过location.hash获得B的高度。(location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url,用location.href=url就可以直接将页面重定向url。而location.hash则可以用来获取或设置页面的标签值。比如http://domain/#admin的location.hash=”#admin”。利用这个属性值可以做一些非常有意义的事情。)。由于iframeB和mail页面同域,所以可以修改mail的dom节点属性,从而达到我们设置iframe标签高度的目的。

 

关键代码:

iframe主页面main.html

<iframe id="iframeB"  name="iframeB" src="www.b.com/B.html" width="100%" height="auto" scrolling="no" frameborder="0"></iframe>

iframe嵌套页面B.html

<iframe id="iframeA" name="iframeA" src="" width="0" height="0" style="display:none;" ></iframe>

<script type="text/javascript">
function sethash(){
    hashH = document.documentElement.scrollHeight; //获取自身高度
    urlC = "www.a.com/A.html"; //设置iframeA的src
    document.getElementById("iframeA").src=urlC+"#"+hashH; //将高度作为参数传递
}
window.onload=sethash;
</script>

中介页面A.html

<script>
function  pseth() {
    var iObj = parent.parent.document.getElementById('iframeB');//A和main同域,所以可以访问节点
    iObjH = parent.parent.frames["iframeB"].frames["iframeA"].location.hash;//访问自己的location对象获取hash值
    iObj.style.height = iObjH.split("#")[1]+"px";//操作dom
}
pseth();
</script>

演示地址:http://blog.jnecw.com/mail/main.html

同域情况下就不用多说了,直接在被嵌套的页面B中获取其自身高度并操作其父窗口main的dom属性即可。

跨浏览器实现float:center,No CSS hacks

2011年11月7日 没有评论

上周做一个分页按钮,页数不固定,要求居中显示,用ul->li实现

们希望实现li是浮动的,并且居中的(li个数不固定,ul宽度未知)。可以设置ul的text-align:center,再设置li的display,可以实现居中,但这样不是我们的初衷,我们需要实现float:center。

这里我们得先重温一下position:relative,它将依据left,right,top,bottom等属性在正常文档流中偏移位置。那 我们可以让ul为position:relative;left:50%,然后再让li像左浮动,在让它 position:relative;right:50%(或者left:-50%),那么li就像向中间浮动一样居中了。

代码如下:

ul, li {
    list-style: none outside none;
    margin: 0;
    padding: 0;
}
#macji {
    overflow: hidden;
    text-align: center;
    width: 100%;
}

#macji .macji-skin {
    float: left;
    left: 50%;
    position: relative;
}

#macji .macji-skin li {
    float: left;
    position: relative;
    right: 50%;
}
<div id="macji">
	<ul class="macji-skin">

        <li>列表二</li>
        <li>列表三列表三列表三列表三</li>
        <li>这里可能是N</li>
    </ul>
</div>
分类: CSS 标签: ,