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 相关文章推荐
javascript[js]获取url参数的代码
Oct 17 Javascript
jQuery ajax serialize()方法的使用以及常见问题解决
Jan 27 Javascript
Vuex2.0+Vue2.0构建备忘录应用实践
Nov 30 Javascript
jQuery插件HighCharts实现的2D对数饼图效果示例【附demo源码下载】
Mar 09 Javascript
angular学习之从零搭建一个angular4.0项目
Jul 10 Javascript
纯JS实现出生日期[年月日]下拉菜单效果
Jun 01 Javascript
VUE DOM加载后执行自定义事件的方法
Sep 07 Javascript
JavaScript 对引擎、运行时、调用堆栈的概述理解
Oct 22 Javascript
浅析Vue中拆分视图层代码的5点建议
Aug 15 Javascript
axios 实现post请求时把对象obj数据转为formdata
Oct 31 Javascript
微信小程序实现可拖动悬浮图标(包括按钮角标的实现)
Dec 29 Javascript
Vue中使用import进行路由懒加载的原理分析
Apr 01 Vue.js
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显示今天、今月、上月、今年的起点/终点时间戳的代码
2011/05/25 PHP
php生成图形(Libchart)实例
2013/11/06 PHP
php+ajax无刷新上传图片实例代码
2015/11/17 PHP
分享php多功能图片处理类
2016/05/15 PHP
PHP实现在对象之外访问其私有属性private及保护属性protected的方法
2017/11/20 PHP
js 判断一组日期是否是连续的简单实例
2016/07/11 Javascript
jquery实现(textarea)placeholder自动换行
2016/12/22 Javascript
easyUI combobox实现联动效果
2017/01/17 Javascript
微信小程序之拖拽排序(代码分享)
2017/01/21 Javascript
javascript阻止事件冒泡和浏览器的默认行为
2017/01/21 Javascript
ajax实现加载页面、删除、查看详细信息 bootstrap美化页面!
2017/03/14 Javascript
JavaScript数据结构之广义表的定义与表示方法详解
2017/04/12 Javascript
JavaScript创建对象的七种方式(推荐)
2017/06/26 Javascript
Vue 2.0学习笔记之使用$refs访问Vue中的DOM
2017/12/19 Javascript
webstorm中配置nodejs环境及npm的实例
2018/05/15 NodeJs
在vue中安装使用vux的教程详解
2018/09/16 Javascript
vue实现todolist功能、todolist组件拆分及todolist的删除功能
2019/04/11 Javascript
vue实现列表滚动的过渡动画
2020/06/29 Javascript
[45:56]Ti4正赛第一天 VG vs NEWBEE 3
2014/07/19 DOTA
[01:08:48]LGD vs OG 2018国际邀请赛淘汰赛BO3 第三场 8.25
2018/08/29 DOTA
python flask 多对多表查询功能
2017/06/25 Python
Python IDE Pycharm中的快捷键列表用法
2019/08/08 Python
快速解决docker-py api版本不兼容的问题
2019/08/30 Python
Pycharm中Python环境配置常见问题解析
2020/01/16 Python
python实现飞行棋游戏
2020/02/05 Python
python GUI库图形界面开发之PyQt5输入对话框QInputDialog详细使用方法与实例
2020/02/27 Python
如何设置PyCharm中的Python代码模版(推荐)
2020/11/20 Python
python推导式的使用方法实例
2021/02/28 Python
常用的四种CSS透明属性介绍
2014/04/12 HTML / CSS
资生堂美国官网:Shiseido美国
2016/09/02 全球购物
精致的手工皮鞋:Shoe Embassy
2019/11/08 全球购物
机电专业毕业生求职信
2013/10/27 职场文书
高中地理教学反思
2014/01/29 职场文书
《菜园里》教学反思
2014/04/17 职场文书
2014年行政工作总结
2014/11/19 职场文书
关于上班时间调整的通知
2015/04/23 职场文书