是开头都会说的原理
瀑布流布局有两种,一种是固定列,一种是非固定列。在此主要记述第一种的实现。
固定列的特征是:无论页面如何缩放,每行的总列数都一致。
一行4列的瀑布流从布局的角度来说,就是4个li标签。通过一定的事件(比如滚动条滚动多少px),然后读取之,再把数据动态地添加到页面中。
添加数据原则,不是根据li索引值来加,而是根据各列中高度最短的的那列动态添加。否则可能导致页面很难看(左右高度不统一)。
实例涉及ajax方法。可在服务器环境下运行。
废话不多说了。直接上样式。
<ul id="ul1"> <li> <div> <img src="/UploadFiles/2021-04-02/1.jpg">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; }基本效果如图:
样式显示没问题之后,就把li里面的代码删掉。
接下来通过ajax来动态添加。
数据哪里来?
这里用的是wookmark的数据接口。
http://www.wookmark.com/api/json/popular"htmlcode">
function createUrl(num){ return 'http://www.wookmark.com/api/json/popular"text-align: center">原来是一个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="/UploadFiles/2021-04-02/'+data[i].preview+'">再加载看看,布局就出来了。简单又漂亮。
做到这里,看起来一切都挺好。然而潜伏着一个致命的问题。
for循环惹的祸?
看看console.log的信息。为了分析,我把4个li的高度放进了一个数组:
50张图片分4列,少说平均高度也得有三四千像素。
而到循环结束,程序判断的终点竟然只有令人发指的1000多个px,因为图片加载过程慢于for循环执行速度。虽然demo里的显示还算正常,但这种代码在网速不好时,会造成工作事故。
思路一:可以判断图片是否加载完成。
可以用个定时器监听下,然后用递归实现,我的方案是这样
var index=0; function LoadPic(index){ var $html=$('<div><img src="/UploadFiles/2021-04-02/'+data[index].preview+'">但是,从用户体验的角度来说,等一张图加载完成再进行下一张加载是不友好的。数据提供方都应该直接把图片的高度在服务器处理好,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="/UploadFiles/2021-04-02/'+data[i].preview+'">事实上个人认为这是最简单且用户体验最好的方案。
有了瀑布,还需要流
流的逻辑
往下拉(滚动),第一个底部进入可视区的li,优先加载。
换句话说,最短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"'+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"'+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; } } }) })以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!
富贵资源网 Design By www.hznty.com广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!富贵资源网 Design By www.hznty.com暂无评论...