jquery实现简单的瀑布流布局


Posted in Javascript onDecember 11, 2016

是开头都会说的原理

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

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

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

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

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

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

<ul id="ul1">
 <li>
  <div>
  <img src="images/1.jpg">
  <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p>
  </div>
 </li>
 <li>
  <div>
  <img src="images/2.jpg">
  <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p>
  </div>
 </li>
 <li>
  <div>
  <img src="images/3.jpg">
  <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p>
  </div>
 </li>
 <li>
  <div>
  <img src="images/4.jpg">
  <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p>
  </div>
 </li>
 </ul>

css

*{
 margin:0;
 padding: 0;
}
ul li{
 list-style: none;
}
#ul1{
 width: 1080px;
 margin: 100px auto 0;
}
li{
 width: 247px;
 float: left;
 margin-right: 10px;
}
li div{
 border:1px solid #000;padding:10px;
 margin-bottom:10px;
}
li div img{
 width: 225px;display: block;
}

基本效果如图:

jquery实现简单的瀑布流布局

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

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

数据哪里来?

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

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

点开url得到是一个json。

信息量很大。怎么分析?

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

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

控制台打印结果为:

jquery实现简单的瀑布流布局

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

布局实现

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

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

然后就是getJSON方法

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

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

jquery实现简单的瀑布流布局

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

for循环惹的祸?

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

jquery实现简单的瀑布流布局

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

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

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

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

var index=0;
   function LoadPic(index){
   var $html=$('<div><img src="'+data[index].preview+'"><p>'+data[index].title+'</p></div>')
   $('li').eq(getShortestLi()).append($html);
   var oImg=$html.find('img');
   var t=setInterval(function(){
    if(oImg.height()!=0){//如果加载完了。
    clearInterval(t);
    //console.log([$('li').eq(0).height(),$('li').eq(1).height(),$('li').eq(2).height(),$('li').eq(3).height()])
    if(index<50){
     return LoadPic(index+1);
    }else{
     return false;
    } 
    }else{
    console.log('wait')
    }
   },50)//每隔50ms监听一次
   }
   LoadPic(0);

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

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

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

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

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

有了瀑布,还需要流

流的逻辑

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

jquery实现简单的瀑布流布局

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

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

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

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

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

obj.offset().top

滚动事件

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

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

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

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

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

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

此时的代码是这样的:

//找出高度最小li的索引值
function getShortestLi(){
 var shortest=0;
 for(var i=1;i<4;i++){
 if($('li').eq(i).height()<$('li').eq(shortest).height()){
  shortest=i;
 }
 }
 return shortest;
}
function createUrl(num){
 return 'http://www.wookmark.com/api/json/popular?page='+num+'&callback=?';
}
function getList(n){
 $.getJSON(createUrl(n),function(data){
 //console.log(data);
 for(var i=0;i<data.length;i++){
  var $html=var $html=$('<div><img src="'+data[i].preview+'"><p>'+data[i].title+'</p></div>');
  //console.log(data[i].height);
  $('li').eq(getShortestLi()).append($html);
  dataArr[i].height*=225/dataArr[i].width;
  $html.find('img').css('height',dataArr[i].height+'px');
  $html.find('img').css('width','225px');
 }; 
}
$(function(){
 var pageNum=1;
 getList(pageNum);
 $(window).scroll(function(){
 var $li=$('li').eq(getShortestLi();
 var scrollTop=document.documentElement.scrollTop||document.body.scrollTop;
 if($li.offset().top+$li.height()<document.documentElement.clientHeight+scrollTop){
  pageNum++;
  console.log(pageNum);
  getList(pageNum);
 }
 })
})

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

如厕的逻辑

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

那就做一个开关吧。

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

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

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

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

这一段不贴代码了。

总有流完的一天。

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

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

全部代码如下:

//找出高度最小li的索引值
function getShortestLi(){
 var shortest=0;
 for(var i=1;i<4;i++){
 if($('li').eq(i).height()<$('li').eq(shortest).height()){
  shortest=i;
 }
 }
 return shortest;
}
function createUrl(num){
 return 'http://www.wookmark.com/api/json/popular?page='+num+'&callback=?';
}
var bCheck=false;
function getList(n){
 $.getJSON(createUrl(n),function(data){
 if(data.length==0){
  return false;
 }else{
  for(var i=0;i<data.length;i++){  
  //console.log(data[i].preview);
  var $html=$('<div><img src="'+data[i].preview+'"><p>'+data[i].title+'</p></div>');
  $('li').eq(getShortestLi()).append($html);
  
  $html.find('img').css('height',(data[i].height*225/data[i].width)+'px');
  $html.find('img').css('width','225px'); 
  };
 }
 bCheck=true; 
 });
}
$(function(){
 var pageNum=1;
 getList(pageNum);
 $(window).scroll(function(){
 var $li=$('li').eq(getShortestLi());
 var scrollTop=document.documentElement.scrollTop||document.body.scrollTop;
 //console.log([$li.offset().top+$li.height(),document.documentElement.clientHeight+scrollTop])
 //如果li高度与li到页面顶部的高度之和<可视区高度+滚动距离
 if($li.offset().top+$li.height()<document.documentElement.clientHeight+scrollTop){
  if(bCheck){
  bCheck=false;
  pageNum++;
  //console.log(pageNum);
  getList(pageNum); 
  }else{
  return false;
  } 
 }
 })
})

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
Bookmarklet实现启动jQuery(模仿 云输入法)
Sep 15 Javascript
js解决select下拉选不中问题
Oct 14 Javascript
jQuery中$(function() {});问题详解
Aug 10 Javascript
JS时间特效最常用的三款
Aug 19 Javascript
浅谈javascript中的call、apply、bind
Mar 06 Javascript
JS使用cookie设置样式的方法
Jun 30 Javascript
JavaScript实现的XML与JSON互转功能详解
Feb 16 Javascript
babel的使用及安装配置教程
Feb 22 Javascript
jQuery实现的简单对话框拖动功能示例
Jun 05 jQuery
javascript异步处理与Jquery deferred对象用法总结
Jun 04 jQuery
JavaScript一元正号运算符示例代码
Jun 30 Javascript
three.js利用卷积法如何实现物体描边效果
Nov 27 Javascript
js倒计时显示实例
Dec 11 #Javascript
jQuery实现模拟flash头像裁切上传功能示例
Dec 11 #Javascript
javascript实现将数字转成千分位的方法小结【5种方式】
Dec 11 #Javascript
JavaScript获取服务器时间的方法详解
Dec 11 #Javascript
基于jQuery实现的查看全文功能【实用】
Dec 11 #Javascript
AngularJS过滤器filter用法分析
Dec 11 #Javascript
jquery判断页面网址是否有效的两种方法
Dec 11 #Javascript
You might like
php给图片添加文字水印方法汇总
2015/08/27 PHP
Laravel框架实现定时发布任务的方法
2018/08/16 PHP
PHP程序守护进程化实现方法详解
2020/07/16 PHP
ASP.NET中基于JQUERY的高性能的TreeView补充
2011/02/23 Javascript
jquery弹出关闭遮罩层实例
2013/08/06 Javascript
jquery获取div距离窗口和父级dv的距离示例
2013/10/10 Javascript
JQuery删除DOM节点的方法
2015/06/11 Javascript
nodejs爬虫抓取数据乱码问题总结
2015/07/03 NodeJs
jquery easyui datagrid实现增加,修改,删除方法总结
2016/05/25 Javascript
微信小程序 五星评分(包括半颗星评分)实例代码
2016/12/14 Javascript
js实现自定义进度条效果
2017/03/15 Javascript
jquery编写日期选择器
2017/03/16 Javascript
vue+vuex+axio从后台获取数据存入vuex实现组件之间共享数据
2017/04/22 Javascript
Javascript中click与blur事件的顺序详析
2017/04/25 Javascript
微信小程序实现折叠与展开文章功能
2018/06/12 Javascript
详解Node.js 中使用 ECDSA 签名遇到的坑
2018/11/26 Javascript
JavaScript解析及序列化JSON的方法实例分析
2019/01/04 Javascript
基于vue、react实现倒计时效果
2019/08/26 Javascript
VUE动态生成word的实现
2020/07/26 Javascript
Python中的rfind()方法使用详解
2015/05/19 Python
浅谈Python实现2种文件复制的方法
2018/01/19 Python
Python基于更相减损术实现求解最大公约数的方法
2018/04/04 Python
Django学习教程之静态文件的调用详解
2018/05/08 Python
解决python中 f.write写入中文出错的问题
2018/10/31 Python
Python使用Turtle库绘制一棵西兰花
2019/11/23 Python
Django实现whoosh搜索引擎使用jieba分词
2020/04/08 Python
详解使用双缓存解决Canvas clearRect引起的闪屏问题
2019/04/29 HTML / CSS
美国排名第一的葡萄酒俱乐部:Firstleaf Wine Club
2020/01/02 全球购物
教师岗位职责
2013/11/17 职场文书
文科教师毕业的自我评价
2014/01/16 职场文书
大学军训感言300字
2014/03/09 职场文书
离婚协议书该怎么写
2014/10/04 职场文书
2014年幼儿园保育工作总结
2014/12/02 职场文书
酒店财务经理岗位职责
2015/04/08 职场文书
学校安全管理制度
2015/08/06 职场文书
Pandas加速代码之避免使用for循环
2021/05/30 Python