写一个jQuery瀑布流布局

这是我毕业闲着没事写着玩的,后面转移博客系统,处理过后也不知道跑步跑得起来。
这里需要注意的是,也是我当时忽略了的一个点:就是我们应该先拿到图片的宽高再进行渲染,不然第一次加载是会有问题的。
但是呢,图片如果很大的话,我们就会出现渲染阻塞的情况,所以我们需要在下载完成之前我们就应该拿到相关宽高数据。
你也可以到我的GitHub下载自己捣腾,点此下载。
一、准备工作
要求:
1.布局要随着窗口大小的改变而自适应。
2.当页面滑动到底部时,自动加载。
原理:
在写这个之前我们要明白其原理,网上有很多关于瀑布流原理相关的文章,大致是三种,差不多都是引用淘宝UED的,但该文的原文链接已经挂了,在网上大家可以搜到大致的文章。我在这里也不多做赘述,简单的把我所用到的原理写下来。
1.子元素使用绝对定位
说明:之所以使用绝对定位,因为我们要随着屏幕的改变而改变子元素在屏幕中的位置。所以选择使用绝对定位,这也是线上最优的一种方法。具体图示:
图片中所说的元素出现的顺序与实际图片出现的顺序有所出入,下面我会做详细的说明。
2.计算好子元素宽度及margin
在写代码的过程中,我们要确定好子元素的宽度,可以使用代码定义,也可以写死,我写的这个例子就是写死了得。宽度固定式200px,margin定的10px。其中的图片定的是180px,margin同样定义的是10。之所以需要这个宽度,确定子元素在X轴上的位置就需要靠他来定位的。当然一行能摆放多少个也需要用到它。
3.计算好所有子元素的高度
这里计算的高度是用来确定子元素在Y轴上的定位,当然我们按照宽度定义的第一行top都为零。高度逐行递加保存。但需要注意的是第n+1行如何摆放是按照第n行最低高度开始逐级摆放的。这里第n行的高度是之前所有同一列子元素高度的和。
4.按照最低高度逐级摆放
5.判断滚动条位置
当滚动条到达底部时,我们向页面添加新的子元素。新的子元素依然要获取它的高度,并将它与页面初始化高度相加。
优缺点:
优点
- 是最优的一种方案,方便添加数据内容;
- 窗口变化,列数/数据块都会自动调整;
缺点
- JS 动态计算数据块位置,当窗口缩放频繁,可能会狂耗性能;
- 需要知道子元素的高度;
二、具体实现
首先文档结构
HTML
<div class="main-box">
<div class="son-box"><img src="img/img_001.jpg" alt=""></div>
<div class="son-box"><img src="img/img_002.jpg" alt=""></div>
<div class="son-box"><img src="img/img_003.jpg" alt=""></div>
<div class="son-box"><img src="img/img_004.jpg" alt=""></div>
<div class="son-box"><img src="img/img_005.jpg" alt=""></div>
<div class="son-box"><img src="img/img_006.jpg" alt=""></div>
.....
</div>
CSS
body {
background-color: aqua;
}
.main-box {
position: relative;
margin: 20px auto 0;
min-width: 420px;
max-width: 1200px;
height: auto;
}
.main-box .son-box {
position: absolute;
box-sizing: border-box;
display: inline-block;
*display: block;
*zoom: 1;
width: 200px;
background-color: #fff;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
-webkit-transition: all 0.7s ease-in 0.1s;
-moz-transition: all 0.7s ease-in 0.1s;
-o-transition: all 0.7s ease-out 0.1s;
transition: all 0.7s ease-out 0.1s;
}
.main-box .son-box > img {
width: 180px;
height: auto;
margin: 10px;
}
好了,具体的文档结构就是这个样子,你也可以根据自己的喜好来调整。那下面就具体的来解析一下JS
var $mainBox = $(".main-box");
var _sonBox = {};
//获取所有子元素
_sonBox.sonAll = $(".son-box");
// /获取子元素宽度及间距
_sonBox.sonW = _sonBox.sonAll[0].offsetWidth + 10;
//定义一个数组,用来保存页面初始化生成的高度
_sonBox.Arr = [];
function CalAgg() {
//主盒子布局
if (document.body.clientWidth < 1200) {
//当页面宽度小于1200px时根据窗口大小决定一行子元素的数量
_sonBox.sonAgg = parseInt(document.body.clientWidth / _sonBox.sonW);
//计算出这一行实际占用多大的宽度并赋值给子元素的父元素,用于水平居中
$mainBox.width(_sonBox.sonW * _sonBox.sonAgg);
}
else {
//如果窗口大于1200px的话,那么久固定一行显示5个
_sonBox.sonAgg = 5;
$mainBox.width(_sonBox.sonW * _sonBox.sonAgg);
}
};
function Waterfall() {
//记录高度
var ArrHeight = [];
//计算每行最大包含多少元素
CalAgg();
for (var i = 0; i < _sonBox.sonAll.length; i++) {
//循环到每个子元素,并获取它的高度,并保存
_sonBox.sonH = _sonBox.sonAll.eq(i).height();
if (i < _sonBox.sonAgg) {
//判断是否为第一行,因为第一行最多是_sonBox.sonAgg个。
// 将获取的子元素高度保存到数组
ArrHeight[i] = _sonBox.sonH;
_sonBox.sonAll.eq(i).css("top", "0");
//在这里的i不可能超过_sonBox.sonAgg,那么按照顺序乘以单个子元素的宽度就是第一行的定位。
_sonBox.sonAll.eq(i).css("left", i * _sonBox.sonW + "px");
}
else {
//获取上一层最小高度
_sonBox.minH = Math.min.apply({}, ArrHeight);
//获取最小高度保存在数组中的位置
_sonBox.minPos = jQuery.inArray(_sonBox.minH, ArrHeight);
//重新赋值高度
ArrHeight[_sonBox.minPos] += _sonBox.sonH + 10;
//该子元素的Y轴定位就是最小高度的下面
_sonBox.sonAll.eq(i).css("top", _sonBox.minH + 10 + "px");
//该子元素的X轴定位就是数组中最小高度的index值乘以子元素的宽度。
_sonBox.sonAll.eq(i).css("left", _sonBox.minPos * _sonBox.sonW + "px");
}
}
//在这里将这个数组复制给另外一个数组,在滚动加载中使用
_sonBox.Arr = ArrHeight.concat();
};
window.onscroll = function () {
//获取滚动条到底部的距离
var _bot = $(document).height() - $(window).height();
//判断滚动条是否到达底部
if ($(document).scrollTop() == _bot) {
//CalAgg();
// 每次增加10条数据
for (var i = 0; i < 10; i++) {
//获取上一层最小高度
_sonBox.minH = Math.min.apply({}, _sonBox.Arr);
//获取最小高度保存在数组中的位置
_sonBox.minPos = jQuery.inArray(_sonBox.minH, _sonBox.Arr);
//添加样式及拼接字符串
var _sonStyle = "top:" + (_sonBox.minH + 10) + "px;" + "left:" + _sonBox.minPos * _sonBox.sonW + "px;",
_sonHtml = '<div class="son-box" style=' + _sonStyle + '><img src=' + _imgObj.imgUrl[i].Url + ' alt=""></div>';
//向父元素结尾处添加
$mainBox.append(_sonHtml);
//重新赋值高度,在循环中使用eq(-1)是获取子元素集合最后一个元素的高度,即在上一步中添加的新子元素的高度
_sonBox.sonH = _sonBox.sonAll = $(".son-box").eq(-1).height();
_sonBox.Arr[_sonBox.minPos] += _sonBox.sonH + 10;
}
//重新获取所有子元素
_sonBox.sonAll = $(".son-box");
}
};
// 在页面加载完成中调用,必须使用onload,等图片加载完成才知道子元素高度是多少。
window.onload = function () {
Waterfall();
};
//当窗口改变时调用
window.onresize = function () {
Waterfall();
};
这里是写完后的效果:戳这里
那么这么一个瀑布流的基本效果也就完成了,当然写的有点坑爹,代码优化也不太好,还有些小BUG时常出没,但大致使用是没有问题的。
在后面我也会写成一个插件,当然这个东西不推荐生产开发,毕竟网上有许许多多优秀的瀑布流插件。这个东西只是给跟我一样的小伙伴们练练手,参考参考。