jQuery瀑布流详解(PC及移动端)
前言
瀑布流布局已成为当今非常普遍的图片展示方式,无论是PC还是手机等移动设备上。这种布局图片的样式大概分为三种:等高等宽、等宽不等高、等高不等宽,接下来我们就最为普遍的等宽不等高形式来作为示例。
我们用百度图片作为范例:

这就是PC端比较常见的瀑布流布局方式,接下来我们审查下元素看看百度图片里面是如何布局:

可以看到,它里面实际是若干个等宽的列容器,通过计算将图片push到不同的容器里。而本文介绍的展示方法是通过定位的方式,虽然最后布局展示的方式不同,但之前的算法都比较类似。
动手
首先我们将如下样式的若干个单元写进body中,并将“box”向左浮动:
<div class="box">
<img class="img" src="./resource/images/1.jpg" />
<div class="desc">Description</div>
</div>
<div class="box">
<img class="img" src="./resource/images/2.jpg" />
<div class="desc">Description</div>
</div>
<div class="box">
<img class="img" src="./resource/images/3.jpg" />
<div class="desc">Description</div>
</div>
得到如下效果:

接下来:
var boxArr = $('.box'),
num = Math.floor(document.body.clientWidth / boxArr.eq(0).outerWidth(true)),
columnHeightArr = [];
columnHeightArr.length = num;
boxArr.each(function(index, item) {
if (index < num) {
columnHeightArr[index] = $(item).position().top + $(item).outerHeight(true);
} else {
var minHeight = Math.min.apply(null, columnHeightArr),
minHeightIndex = $.inArray(minHeight,columnHeightArr);
$(item).css({
position: 'absolute',
top: minHeight,
left: boxArr.eq(minHeightIndex).position().left
});
}
});
以上代码大意为:
1. 首先计算出在浏览器中一行能容纳多少图片 (num) ,注意这里用了outerWidth,当传入true时会返回元素包括margin、padding、border全部盒模型属性的尺寸;
2. 创建一个存储每一列高度的数组 (columnHeightArr) ,该数组的长度即为num值;
3. 遍历所有图片,将第一行的图片高度分别存入列高数组中 (columnHeightArr) ,从第二行开始,首先计算出所有列中最小的高度 (minHeight) 以及最小高度所在的列 (minHeightIndex)。之后将第二行开始的图片定位在高度最小列的下面,效果如下:

可以看到虽然摆对了地方但是所有的图片都放在同一个地方了,这是因为我们需要在摆放一张图片后就要增加该列的高度:
var boxArr = $('.box'),
num = Math.floor(document.body.clientWidth / boxArr.eq(0).outerWidth(true)),
columnHeightArr = [];
columnHeightArr.length = num;
boxArr.each(function(index, item) {
if (index < num) {
columnHeightArr[index] = $(item).position().top + $(item).outerHeight(true);
} else {
var minHeight = Math.min.apply(null, columnHeightArr),
minHeightIndex = $.inArray(minHeight, columnHeightArr);
$(item).css({
position: 'absolute',
top: minHeight,
left: boxArr.eq(minHeightIndex).position().left
});
columnHeightArr[minHeightIndex] += $(item).outerHeight(true);
}
});
结果正确:

注意:上面的代码需要运行于window.onload方法里,因为只有当页面中的图片资源全部加载完毕后,其每张图片的高度才会有效。
因此会有一些很严重的问题,当网络不好的时候图片没有完全加载完成就会出现图片展示不全高度缺失的情况,这点在移动端很明显。而且当我们加载更多时,更难判断新追加的图片是否加载完成。
在实际生产中更不会有一开始就将图片写死在HTML中的情况,所以我们通常用以下的方式来做:
首先我们在获得图片地址时同时也需要获取图片的宽和高 ,这点对服务端后台来说并不是什么难事,可以拜托后台兄弟将图片的宽高数据拼进JSON,传递给你;
*接下来介绍小技巧,是一个朋友教我的,非常实用,它能保证一个元素无论大小如何变化,比例始终保持一致。这个技巧尤其适用于移动端,因为元素为了响应式通常使用百分比的形式。
假如手机页面中有一张图片,其宽度要为屏幕的一半,高宽比为2:1,需要在任何分辨的手机上保持比例不变。如何做?给元素设置如下属性:
.box {
width: 50%;
height:;
padding-bottom: 100%;
}
不设置高度,而是用padding“挤”出元素高度,而padding的百分比值都是基于父级容器的宽度。padding需要挤多少呢?就是宽度乘以高宽比(width和padding值均为百分比值),这就是我们为什么需要获得图片尺寸的原因。
效果:


可以看到在chrome手机模拟器中ipone4和肾6Plus的显示效果是完全一样的。在手机页面中宽是固定的,而高会随着页面内容的多少而变化,这个技巧利用元素padding百分比的值其实是基于其父级容器的宽,将高的值巧妙的转化成与宽相关。
说到现在可能有人终于忍不住要问了,讲了这么多和瀑布流有什么关系!简单就是一句话,我们要抛弃 img 标签,而采用背景图的方式。为了使用背景图,就得保持元素的比例永远与图片保持一致。
通过这种方式,可以不用判断图片都加载完毕,直接产生一些与图片同比例的div,再为其设置背景图,如下:

这里比如最外层的box宽度为220px,里面的img元素宽度就可以为100%,高度就可以通过padding挤出了。
懒加载
使用背景图的方式还有好处那就是可以比较方便的实现懒加载。那什么是懒加载呢?就是当元素在我们的视野中时才展示图片,滚动时屏幕下方的图片并不展示,这可以很好的增加加载速度提升体验。
首先我们给最外层的box增加一个box-item类名(之后有用),将图片url并不设置给backgroundImage属性,而是赋给一个自定义属性:data-src。
<div class="box box-item">
<div class="img" data-src="./resource/images/1.jpg"></div>
<div class="desc">Description</div>
</div>
接下来我们编写懒加载函数:
function lazyLoad() {
var boxArr = $('.box-item');
boxArr.each(function(index, item) {
var viewTop = $(item).offset().top - $(window).scrollTop(),
imgObj = $(item).find('.img');
if ((viewTop < $(window).height()) && (($(item).offset().top + $(item).outerHeight(true)) > $(window).scrollTop())) {
imgObj.css('backgroundImage','url('+imgObj.attr("data-src")+')').removeClass('data-src');
$(item).removeClass('box-item');
}
})
}
首先我们获取所有拥有 .box-item 类名的元素,遍历。viewTop 为图片相对于浏览器窗口的相对高度,类似于position:fixed感觉。
通过条件进行判断,只有当该图片在浏览器窗口内(之上或之下都不行)时,将需要设置背景图元素的 data-src 值展示出来,并删除该属性。
之后将最外层元素的 box-item 删除,因为已经展示出来的图片不需要再进行这些判断,删除了该类名下一次滚动时就不会获取到已经展示过的元素,需要遍历的次数就会越来越少,这样能起到一个优化的作用。
该函数需要在你的元素已经append进页面时调用,以及在滚动时调用:
lazyLoad();
$(window).scroll(lazyLoad);
滚动加载
说完了懒加载,再说说滚动加载。所谓滚动加载就是当页面滚动到底部附近时加载新的图片。我这里选择的是滚动到高度最小的列底部时加载新的数据,你也可以根据自己的喜好来做判断。
function scrollLoad() {
var viewHeight = $(window).scrollTop() + $(window).height(),
minHeight = Math.min.apply(null, columnHeightArr);
if (viewHeight >= minHeight) {
//loadMore...
}
}
滚动加载也是在window的滚动事件中进行监听,可以与懒加载一起进行:
$(window).scroll(function() {
scrollLoad();
lazyLoad();
});
说完了PC端,我们来说下手机端。其实原理是一样的,只是从多列变成固定的两列了。
var boxArr = $('.box'),
columnHeightArr = [];
columnHeightArr.length = 2;
boxArr.each(function(index, item) {
if (index < 2) {
columnHeightArr[index] = $(item).position().top + $(item).outerHeight(true);
} else {
var minHeight = Math.min.apply(null, columnHeightArr),
minHeightIndex = $.inArray(minHeight, columnHeightArr);
$(item).css({
position: 'absolute',
top: minHeight,
left: boxArr.eq(minHeightIndex).position().left
});
columnHeightArr[minHeightIndex] += $(item).outerHeight(true);
}
});
不同的是为了适应不同屏幕的手机,最外层的box容器宽度和边距要设置成百分比的形式。

最后有一点要注意,因为我们没有像百度一样用一个个列盒子去装,而是用定位的方式。导致的问题是图片元素的父级没法自适应高度,如果你有相关的需求我们可以计算出所有列中最长的长度,并将这个值赋值给父容器的min-height属性:
$('body').css('minHeight',Math.max.apply(null, columnHeightArr));
整理下完整的代码,瀑布流的全套服务就到这了 :)
var dataArr = [
{picUrl:'./resource/images/1.jpg',width:522,height:783},
{picUrl:'./resource/images/2.jpg',width:550,height:786},
{picUrl:'./resource/images/3.jpg',width:535,height:800},
{picUrl:'./resource/images/4.jpg',width:578,height:504},
{picUrl:'./resource/images/5.jpg',width:1440,height:900}
]; $.each(dataArr, function(index, item) {
$("body").append('<div class="box box-item">' +
'<div class="img" style="height:0;padding-bottom:'+cRate(item) * 100 + "%"+'" data-src="'+item.picUrl+'"></div>' +
'<div class="desc">Description</div>' +
'</div>');
}); var boxArr = $('.box'),
num = Math.floor(document.body.clientWidth / boxArr.eq(0).outerWidth(true)),
columnHeightArr = [];
columnHeightArr.length = num; arrangement(); $('body').css('minHeight',Math.max.apply(null, columnHeightArr)); lazyLoad(); function arrangement() {
boxArr.each(function(index, item) {
if (index < num) {
columnHeightArr[index] = $(item).position().top + $(item).outerHeight(true);
} else {
var minHeight = Math.min.apply(null, columnHeightArr),
minHeightIndex = $.inArray(minHeight, columnHeightArr);
$(item).css({
position: 'absolute',
top: minHeight,
left: boxArr.eq(minHeightIndex).position().left
});
columnHeightArr[minHeightIndex] += $(item).outerHeight(true);
}
});
} function lazyLoad() {
var boxArr = $('.box-item');
boxArr.each(function(index, item) {
var viewTop = $(item).offset().top - $(window).scrollTop(),
imgObj = $(item).find('.img');
if ((viewTop < $(window).height()) && ($(item).offset().top + $(item).outerHeight(true) > $(window).scrollTop())) {
// console.log($(item).attr('data-src'));
imgObj.css('backgroundImage','url('+imgObj.attr("data-src")+')').removeClass('data-src');
$(item).removeClass('box-item');
}
})
} function cRate(obj) {
return obj.height / obj.width;
} function scrollLoad() {
var viewHeight = $(window).scrollTop() + $(window).height(),
minHeight = Math.min.apply(null, columnHeightArr);
if (viewHeight >= minHeight) {
//loadMore...
}
} $(window).scroll(function() {
lazyLoad();
scrollLoad();
});
感谢您的浏览,希望能给您带来帮助。
jQuery瀑布流详解(PC及移动端)的更多相关文章
- jquery的css详解(二)
jq的工具方法style用于设置样式,jq的实例方法css在设置样式时就是调用的它,接下来分析一下源码. jQuery.extend({ ............................ st ...
- jQuery.attr() 函数详解
一,jQuery.attr() 函数详解: http://www.365mini.com/page/jquery-attr.htm 二,jQuery函数attr()和prop()的区别: http: ...
- jQuery瀑布流从不同方向加载3种效果演示
很实用的一款插件jQuery瀑布流从不同方向加载3种效果演示在线预览 下载地址 实例代码 <section class="grid-wrap"> <ul clas ...
- jQuery 事件用法详解
jQuery 事件用法详解 目录 简介 实现原理 事件操作 绑定事件 解除事件 触发事件 事件委托 事件操作进阶 阻止默认事件 阻止事件传播 阻止事件向后执行 命名空间 自定义事件 事件队列 jque ...
- 8款实用的Jquery瀑布流插件
1.网友Null分享Jquery响应式瀑布流布局插件 首先非常感谢网友Null的无私分享,此作品是一款响应式瀑布流布局Jquery插件,网友Null增加了一个屏幕自适应和响应式,响应式就是支持智能手机 ...
- jquery瀑布流排列样式代码
<!DOCTYPE html><html><head lang="en"> <meta charset="gb2312" ...
- jQuery.ready() 函数详解
jQuery.ready() 函数详解 ready()函数用于在当前文档结构载入完毕后立即执行指定的函数. 该函数的作用相当于window.onload事件. 你可以多次调用该函数,从而绑定多个函数, ...
- jquery inArray()函数详解
jquery inarray()函数详解 jquery.inarray(value,array)确定第一个参数在数组中的位置(如果没有找到则返回 -1 ). determine the index o ...
- 基于JavaSE阶段的IO流详解
1.IO流基本概述 在Java语言中定义了许多针对不同的传输方式,最基本的就是输入输出流(俗称IO流),IO流是属于java.io包下的内容,在JavaSE阶段主要学下图所示的: 其中从图中可知,所有 ...
随机推荐
- September 30th 2017 Week 39th Saturday
The simplest answer is often the correct one. 最简单的答案通常是最正确的答案. Simplest is always best. Sometimes yo ...
- #001 GIT创建分支
GitHub创建分支 如何在github 创建 一个分支出来,进行开发,最后在 merge 到主干上. Git 操作命令 git branch branchName //创建一个分支 git chec ...
- Hadoop HA on Yarn——集群启动
这里分两部分,第一部分是NameNode HA,第二部分是ResourceManager HA (ResourceManager HA是hadoop-2.4.1之后加上的) NameNode HA 1 ...
- CSS盒子模型之CSS3可伸缩框属性(Flexible Box)
CSS盒子模型(下) 一.CSS3可伸缩框(Flexible Box) 可伸缩框属性(Flexible Box)是css3新添加的盒子模型属性,有人称之为弹性盒模型,它的出现打破了我们经常使用的浮动布 ...
- 2049. [SDOI2008]Cave 洞穴勘测【LCT】
Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好 ...
- 【洛谷】【堆】P1801 黑匣子_NOI导刊2010提高(06)
[题目描述:] Black Box是一种原始的数据库.它可以储存一个整数数组,还有一个特别的变量i.最开始的时候Black Box是空的.而i等于0.这个Black Box要处理一串命令. 命令只有两 ...
- 4719: [Noip2016]天天爱跑步
Time Limit: 40 Sec Memory Limit: 512 MB Submit: 1986 Solved: 752 [Submit][Status][Discuss] Descripti ...
- ngRouter和ui-router区别
在单页面应用中要把各个分散的视图给组织起来是通过路由机制来实现的.本文主要对 AngularJS 原生的 ngRoute 路由模块和第三方路由模块 ui.router 的用法进行简单介绍,并做一个对比 ...
- Java并发编程 -- 文章汇总
文章汇总 1.Thread和Runnable 2.synchronized 3.Lock 4.Executor框架 5.信号量和障碍器 6.Exchanger线程间交换数据 7.Java内存操作总结
- kendo ui - DatePicker 日期时间系列
kendo-ui 官网:https://www.telerik.com/documentation 初始化 grid: 引入文件: <link rel="stylesheet" ...