jquery实现简单瀑布流布局

是开头都会说的原理

瀑布流布局有两种,一种是固定列,一种是非固定列。在此主要记述第一种的实现。

固定列的特征是:无论页面如何缩放,每行的总列数都一致。

一行4列的瀑布流从布局的角度来说,就是4个li标签。通过一定的事件(比如滚动条滚动多少px),然后读取之,再把数据动态地添加到页面中。

添加数据原则,不是根据li索引值来加,而是根据各列中高度最短的的那列动态添加。否则可能导致页面很难看(左右高度不统一)。

实例涉及ajax方法。可在服务器环境下运行。

废话不多说了。直接上样式。

  1. <ul id="ul1">
  2. <li>
  3. <div>
  4. <img src="images/1.jpg">
  5. <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p>
  6. </div>
  7. </li>
  8. <li>
  9. <div>
  10. <img src="images/2.jpg">
  11. <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p>
  12. </div>
  13. </li>
  14. <li>
  15. <div>
  16. <img src="images/3.jpg">
  17. <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p>
  18. </div>
  19. </li>
  20. <li>
  21. <div>
  22. <img src="images/4.jpg">
  23. <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p>
  24. </div>
  25. </li>
  26. </ul>

css

  1. *{
  2. margin:0;
  3. padding: 0;
  4. }
  5. ul li{
  6. list-style: none;
  7. }
  8. #ul1{
  9. width: 1080px;
  10. margin: 100px auto 0;
  11. }
  12. li{
  13. width: 247px;
  14. float: left;
  15. margin-right: 10px;
  16. }
  17. li div{
  18. border:1px solid #000;padding:10px;
  19. margin-bottom:10px;
  20. }
  21. li div img{
  22. width: 225px;display: block;
  23. }

基本效果如图:



样式显示没问题之后,就把li里面的代码删掉。

接下来通过ajax来动态添加。

数据哪里来?

这里用的是wookmark的数据接口。

http://www.wookmark.com/api/json/popular?page=1

点开url得到是一个json。

信息量很大。怎么分析?

一般可以看文档。但是手头没有文档的情况下,可以看看链接。返回是什么鬼。

  1. function createUrl(num){
  2. return 'http://www.wookmark.com/api/json/popular?page='+num+'&callback=?';
  3. }
  4. $(function(){
  5. $.getJSON(createUrl(1),function(data){
  6. console.log(data);
  7. })
  8. })

控制台打印结果为:



原来是一个50个图片信息组成的数组。每个数组元素都是一个json。在这个简单的demo里面,暂时只需要取preview属性和title属性就好了。

布局实现

关键之一在于,判断最短的li,事实上我们需要最短高度li的索引值。

  1. //找出高度最小li的索引值
  2. function getShortestLi(){
  3. var shortest=0;
  4. for(var i=1;i<4;i++){
  5. if($('li').eq(i).height()<$('li').eq(shortest).height()){
  6. shortest=i;
  7. }
  8. }
  9. return shortest;
  10. }

然后就是getJSON方法

  1. $(function(){
  2. $.getJSON(createUrl(1),function(data){
  3. //console.log(data);
  4. for(var i=0;i<dataArr.length;i++){
  5. var $html=$('<div><img src="'+data[i].preview+'"><p>'+data[i].title+'</p></div>');
  6. //console.log($('li').eq(getShortestLi()).height())
  7. $('li').eq(getShortestLi()).append($html);
  8. };
  9. console.log([$('li').eq(0).height(),$('li').eq(1).height(),$('li').eq(2).height(),$('li').eq(3).height()])
  10. })
  11. })

再加载看看,布局就出来了。简单又漂亮。

做到这里,看起来一切都挺好。然而潜伏着一个致命的问题。

for循环惹的祸?

看看console.log的信息。为了分析,我把4个li的高度放进了一个数组:



50张图片分4列,少说平均高度也得有三四千像素。

而到循环结束,程序判断的终点竟然只有令人发指的1000多个px,因为图片加载过程慢于for循环执行速度。虽然demo里的显示还算正常,但这种代码在网速不好时,会造成工作事故。

思路一:可以判断图片是否加载完成。

可以用个定时器监听下,然后用递归实现,我的方案是这样

  1. var index=0;
  2. function LoadPic(index){
  3. var $html=$('<div><img src="'+data[index].preview+'"><p>'+data[index].title+'</p></div>')
  4. $('li').eq(getShortestLi()).append($html);
  5. var oImg=$html.find('img');
  6. var t=setInterval(function(){
  7. if(oImg.height()!=0){//如果加载完了。
  8. clearInterval(t);
  9. //console.log([$('li').eq(0).height(),$('li').eq(1).height(),$('li').eq(2).height(),$('li').eq(3).height()])
  10. if(index<50){
  11. return LoadPic(index+1);
  12. }else{
  13. return false;
  14. }
  15. }else{
  16. console.log('wait')
  17. }
  18. },50)//每隔50ms监听一次
  19. }
  20. LoadPic(0);

但是,从用户体验的角度来说,等一张图加载完成再进行下一张加载是不友好的。数据提供方都应该直接把图片的高度在服务器处理好,json数据里面返回过来。网速很慢的时候,要等好久,然后一下子图片都出来了,不觉得很诡异吗?尤其是第三方接口。一加载不出来就出大问题了。

所幸的是,第三方提供了图片的宽高信息。

因此for循环还是可以有的,在返回的数据里面,有宽度和高度值。利用它们就可以实现定宽(255px)和定高(原始高度乘上一个比例)。

  1. $(function(){
  2. $.getJSON(createUrl(1),function(data){
  3. console.log(data);
  4. for(var i=0;i<data.length;i++){
  5. //console.log(data[i].preview);
  6. var $html=$('<div><img src="'+data[i].preview+'"><p>'+data[i].title+'</p></div>')
  7. $('li').eq(getShortestLi()).append($html);
  8. $html.find('img').css('height',(data[i].height*225/data[i].width)+'px');
  9. $html.find('img').css('width','225px');
  10. };
  11. //console.log([$('li').eq(0).height(),$('li').eq(1).height(),$('li').eq(2).height(),$('li').eq(3).height()])
  12. })
  13. })

事实上个人认为这是最简单且用户体验最好的方案。

有了瀑布,还需要流

流的逻辑

往下拉(滚动),第一个底部进入可视区的li,优先加载。



换句话说,最短li的高度与该li到页面顶部之和小于滚动条高度和可视区高度之和时,触发li加载。

li的高度好求。但是最短li到页面顶部距离怎么求?

原生的方法可以这样实现:

  1. function getTop(obj){
  2. var iTop=0;
  3. while(obj){
  4. iTop+=obj.offsetTop;
  5. obj=obj.offsetParent;
  6. }//累加元素本身和自身所有父级高度偏移值
  7. return iTop;
  8. }

但是本案例既然是用jquery,自然有它的方法。

  1. obj.offset().top

滚动事件

原生的实现方法是:window.onscroll=function(){...}

jquery的实现方法是:$(window).scroll(function(){...})

现在验证一下写出的代码代码有没问题

  1. (window).scroll(function(){
  2. var $li=$('li').eq(getShortestLi());
  3. var scrollTop=document.documentElement.scrollTop||document.body.scrollTop;
  4. //console.log([$li.offset().top+$li.height(),document.documentElement.clientHeight+scrollTop])
  5. //如果li高度与li到页面顶部的高度之和<可视区高度+滚动距离
  6. if($li.offset().top+$li.height()<document.documentElement.clientHeight+scrollTop){
  7. alert(1);
  8. }
  9. })

运行代码,发现第一个到底的li出现是可视区时,弹出1.证明可用。

因为涉及到滚动事件,所以getJSON相关函数应该封装为getList()方便调用。所以需要重新调整一下。

此时的代码是这样的:

  1. //找出高度最小li的索引值
  2. function getShortestLi(){
  3. var shortest=0;
  4. for(var i=1;i<4;i++){
  5. if($('li').eq(i).height()<$('li').eq(shortest).height()){
  6. shortest=i;
  7. }
  8. }
  9. return shortest;
  10. }
  11. function createUrl(num){
  12. return 'http://www.wookmark.com/api/json/popular?page='+num+'&callback=?';
  13. }
  14. function getList(n){
  15. $.getJSON(createUrl(n),function(data){
  16. //console.log(data);
  17. for(var i=0;i<data.length;i++){
  18. var $html=var $html=$('<div><img src="'+data[i].preview+'"><p>'+data[i].title+'</p></div>');
  19. //console.log(data[i].height);
  20. $('li').eq(getShortestLi()).append($html);
  21. dataArr[i].height*=225/dataArr[i].width;
  22. $html.find('img').css('height',dataArr[i].height+'px');
  23. $html.find('img').css('width','225px');
  24. };
  25. }
  26. $(function(){
  27. var pageNum=1;
  28. getList(pageNum);
  29. $(window).scroll(function(){
  30. var $li=$('li').eq(getShortestLi();
  31. var scrollTop=document.documentElement.scrollTop||document.body.scrollTop;
  32. if($li.offset().top+$li.height()<document.documentElement.clientHeight+scrollTop){
  33. pageNum++;
  34. console.log(pageNum);
  35. getList(pageNum);
  36. }
  37. })
  38. })

这样一来,好像可以实现了。但是一看控制台的console.log,又发现问题。

如厕的逻辑

在触发加载前提时,图片正在加载,期间动了滚动条,就又触发第二次加载,再动一下,就触发第三次,于是短短一瞬间,触发了n次加载。

那就做一个开关吧。

就跟公厕逻辑一样。n个人排队进一个坑位。外面的人想要进去首先得判断门是否锁上了。没锁才能进。进去之后第一件事把门锁上。等如厕完毕,门就打开。后面的人才能进

新设置一个开关bCheck,默认为true。

到触发加载条件时,还要判断bCheck是否为真(门开),为真时才能触发getList()(如厕)。否则return false(只能等)。

getList一开始就把bCheck设为false(如厕前先锁门)。等到getList回调函数执行到尾声。再把bCheck设为true(开门)。

这一段不贴代码了。

总有流完的一天。

当数据结束时(所有人上完厕所),就没有必要再进行加载了(自动把门锁上)。

所以在getJSON回调函数内锁门之后发现进来的是个空数组,那就进行判断,当获取到data的length为空时,直接returnfalse。那么bCheck就永远关上了。

全部代码如下:

  1. //找出高度最小li的索引值
  2. function getShortestLi(){
  3. var shortest=0;
  4. for(var i=1;i<4;i++){
  5. if($('li').eq(i).height()<$('li').eq(shortest).height()){
  6. shortest=i;
  7. }
  8. }
  9. return shortest;
  10. }
  11. function createUrl(num){
  12. return 'http://www.wookmark.com/api/json/popular?page='+num+'&callback=?';
  13. }
  14. var bCheck=false;
  15. function getList(n){
  16. $.getJSON(createUrl(n),function(data){
  17. if(data.length==0){
  18. return false;
  19. }else{
  20. for(var i=0;i<data.length;i++){
  21. //console.log(data[i].preview);
  22. var $html=$('<div><img src="'+data[i].preview+'"><p>'+data[i].title+'</p></div>');
  23. $('li').eq(getShortestLi()).append($html);
  24. $html.find('img').css('height',(data[i].height*225/data[i].width)+'px');
  25. $html.find('img').css('width','225px');
  26. };
  27. }
  28. bCheck=true;
  29. });
  30. }
  31. $(function(){
  32. var pageNum=1;
  33. getList(pageNum);
  34. $(window).scroll(function(){
  35. var $li=$('li').eq(getShortestLi());
  36. var scrollTop=document.documentElement.scrollTop||document.body.scrollTop;
  37. //console.log([$li.offset().top+$li.height(),document.documentElement.clientHeight+scrollTop])
  38. //如果li高度与li到页面顶部的高度之和<可视区高度+滚动距离
  39. if($li.offset().top+$li.height()<document.documentElement.clientHeight+scrollTop){
  40. if(bCheck){
  41. bCheck=false;
  42. pageNum++;
  43. //console.log(pageNum);
  44. getList(pageNum);
  45. }else{
  46. return false;
  47. }
  48. }
  49. })
  50. })

最后欣赏了下图片,不得不感叹,歪果仁真会玩~

jquery实现简单瀑布流布局的更多相关文章

  1. jquery实现简单瀑布流布局(续):图片懒加载

    # jquery实现简单瀑布流布局(续):图片懒加载 这篇文章是jquery实现简单瀑布流布局思想的小小扩展.代码基于前作的代码继续完善. 图片懒加载就是符合某些条件时才触发图片的加载.最常见的具体表 ...

  2. Ajax+json+jquery实现无限瀑布流布局

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  3. jQuery Wookmark Load 瀑布流布局实例演示

    瀑布流布局非常适合大量图片的展示,一改过去裁剪图片尺寸统一的排版,每张图片都能完全展示,并错落有致,让人眼前一亮. 版本: jQuery v1.4.3+ jQuery Wookmark Load v1 ...

  4. 使用jquery+css实现瀑布流布局

    虽然可以直接使用css实现瀑布流布局,但显示的方式有点问题,所以这儿就直接使用jquery+css来实现瀑布流布局,最终效果如下:      思路是通过将每个小块的position设置为relativ ...

  5. jquery实现简单瀑布流

    瀑布流这个概念一直不是很理解,看到别人可以实现,自己弄了很久还是不能实现就很纠结.瀑布流这根刺就一直扎在我心里,一次偶然的机会看到别人实现了瀑布流,我想我是不是也应该再继续把这个未完成的任务画一个圆满 ...

  6. jquery实现简单瀑布流代码

    测试环境:ie8 ff13.0.1  chrome22 可以将分页获取的内容依次填入四个div中,瀑布流的分页可以以多页(比如5页)为单位二次分页,这样可以减少后台算法的复杂度 <!DOCTYP ...

  7. AJAX+json+jquery实现预加载瀑布流布局

    宽度是一定的高度不定的瀑布流布局 也可以说是无缝拼图 当浏览器滚动到底部时候自动加载图片 加载的图片地址用json 在img.js里 ,还有正在加载动画是用 css3制作的 在ff等支持css3可以显 ...

  8. myWaterfall - jQuery瀑布流布局插件

    myWaterfall - jQuery瀑布流布局插件 Demo http://jsfiddle.net/q3011893/p5k2ogy8/embedded/result,html,css,js/ ...

  9. jQuery Wookmark 瀑布流布局

    瀑布流布局非常适合大量图片的展示,一改过去裁剪图片尺寸统一的排版,每张图片都能完全展示,并错落有致,让人眼前一亮. 版本: jQuery v1.4.3+ jQuery Wookmark Load v1 ...

随机推荐

  1. 大数据系列(4)——Hadoop集群VSFTP和SecureCRT安装配置

    前言 经过前三篇文章的介绍,已经通过VMware安装了Hadoop的集群环境,当然,我相信安装的过程肯定遇到或多或少的问题,这些都需要自己解决,解决的过程就是学习的过程,本篇的来介绍几个Hadoop环 ...

  2. 大数据系统之监控系统(二)Flume的扩展

    一些需求是原生Flume无法满足的,因此,基于开源的Flume我们增加了许多功能. EventDeserializer的缺陷 Flume的每一个source对应的deserializer必须实现接口E ...

  3. Media Queries 详解

    Media Queries直译过来就是“媒体查询”,在我们平时的Web页面中head部分常看到这样的一段代码:  <link href="css/reset.css" rel ...

  4. 使用好压(HaoZip)软件打包EverEdit制作安装程序

    最近使用EverEdit,使用原始的安装程序安装后,需要重新安装插件,对配置文件进行了修改,定制了工具栏.将安装后的程序目录进行打包,制作新的安装包,便于携带. 以下为打包制作过程: 打包原料:Eve ...

  5. Linux吃掉我的内存

    在Windows下资源管理器查看内存使用的情况,如果使用率达到80%以上,再运行大程序就能感觉到系统不流畅了,因为在内存紧缺的情况下使用交换分区,频繁地从磁盘上换入换出页会极大地影响系统的性能.而当我 ...

  6. 洛谷P1280 尼克的任务[DP]

    题目描述 尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成. 尼克的一个工作日为N分钟,从第一分钟开始 ...

  7. 第54课 Qt 中的多页面切换组件

    1. 多页面切换组件(QTabWidget) (1)能够在同一个窗口中自由切换不同页面的内容 (2)是一个容器类型的组件,同时提供友好的页面切换方式 2. QTabWidget的使用方式 (1)在应用 ...

  8. HOLOLENS不适合加天空盒

    加了就有点像VR了,但是视野太窄,所以还是去掉天空盒吧

  9. web前端开发分享-css,js工具篇

    web前端开发乃及其它的相关开发,推荐sublime text, webstorm(jetbrains公司系列产品)这两个的原因在于,有个技术叫emmet, http://docs.emmet.io, ...

  10. Python3的decode()与encode()

    python3的decode()与encode() Tags: Python Python3 对于从python2.7过来的人,对python3的感受就是python3对文本以及二进制数据做了比较清晰 ...