瀑布流布局
一、什么是瀑布流布局
当我们浏览一些图片网站时会发现一些
图片宽度相同、高度不同
,确能够自动地适应,达到一行一行展示的效果。并且当浏览器滑到底部时,新加载的图片也会自动适应。这一效果就是瀑布流布局。瀑布流布局的特点是图片等宽不等高
二、瀑布流布局效果展示
- 三列
- 四列
- 五列
三、瀑布流布局原理
实现瀑布流布局最重要的就是怎么用JS使子盒子定位到合适的位置
接下来我用下面这个示意图向大噶解释瀑布流的基本原理(图丑别介意哈哈)
注意
(1)红色盒子是按顺序排在第一行的子盒子
(2)从第二行开始,将第二行的第一个子盒子追加在第一行最矮盒子的后面;将第二行的第二个子盒子追加在第一行第二矮矮盒子的后面…依次类推。 (下面第一张图)
(3)新追加子盒子的位置要相对父盒子进行定位。其 左距离 为 下标值 * 子盒子宽度
,上距离 为其所跟子盒子的高度 (下面第二张图)
(4)排列好第二行的子盒子后将前两行同列子盒子的高度相加看作新的一行子盒子的高度
,再重复上述步骤。 (下面第三张图)
四、HTML页面布局
很简单的一个页面布局,大噶都能看的懂哈(我这里使用了十六张图片),就不啰(tou)嗦(lan)啦!
<div id="main">
<div class="box">
<div class="pic">
<img src="../program1/images/1.jpg" alt="">
</div>
</div>
<div class="box"><div class="pic"><img src="../program1/images/2.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/3.png" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/4.png" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/1.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/2.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/3.png" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/4.png" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/1.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/2.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/3.png" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/4.png" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/1.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/2.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/3.png" alt=""></div></div>
<div class="box"><div class="pic"><img src="../program1/images/4.png" alt=""></div></div>
</div>
五、CSS样式布局
这里提一些大家需要注意的地方
(1)选择器main 要设置 position: relative; 是为了后续一部分子盒子要进行定位操作(子绝父相)
(2)选择器box 要设置 float: left; 是因为 div 标签是块状元素,使用 float: left; 使其左浮
<style>
* {
padding: 0;
margin: 0;
border: none;
}
img {
width: 200px;
vertical-align: top;
}
#main {
position: relative;
}
.box {
float: left;
padding: 15px 0 0 15px;
}
.pic {
padding: 10px;
border: 1px solid #ccc;
}
</style>
六、JS核心代码
1、全部JS代码展示
window.addEventListener("load",function() {
//1、实现瀑布流布局
waterFall('main','box');
})
})
/*
实现瀑布流布局,传递参数为string类型
*/
function waterFall(parent,child) {
//1、根据图片的列数来确定父盒子的宽度,父盒子居中
//1.1 获取标签,父盒子和所有子盒子
var father= document.getElementById(parent);
var allBox = document.getElementsByClassName(child);
//1.2 获取其中一个的宽度
var boxWidth = allBox[0].offsetWidth;
//1.3 获取文档的宽度(兼容)
var screen = document.documentElement.clientWidth || document.body.clientWidth;
//1.4 求出当前图片的列数,是变化的
var cols = parseInt(screen / boxWidth);
//1.5 父盒子居中,给父盒子设置宽度
father.style.width = cols * boxWidth + 'px';
father.style.margin = '0 auto';
//2、子盒子定位(从第二行开始)
//2.1 定义变量
var heightArr = [], boxHeight = 0, minBoxHeight = 0, minIndex = 0;
//2.2 遍历所有的盒子
for(let i = 0;i < allBox.length; i++) {
boxHeight = allBox[i].offsetHeight;
//2.3 判断是否是第一行
if(i < cols) {
heightArr.push(boxHeight)
}else { //剩余行做定位
//2.4 取出数组中最矮盒子的高度
minBoxHeight = heightArr[minBox(heightArr)];
//2.5 取出最矮盒子再数组中的索引
minIndex = minBox(heightArr);
//2.6 剩余子盒子的定位
allBox[i].style.position = 'absolute';
allBox[i].style.left = minIndex * boxWidth + 'px';
allBox[i].style.top = minBoxHeight + 'px';
//2.7 更新高度
heightArr[minIndex] += boxHeight;
}
}
}
function minBox(box) {
var j = 0;
for(i in box) {
if(box[j] > box[i])
j = i
}
return j;
}
2、JS代码详解----入口函数
在入口函数中调用函数 waterFall() 实现瀑布流布局。
传递两个参数
,都为 String 类型。第一个参数是父盒子的选择器名,第二个参数是子盒子的选择器名。
window.addEventListener("load",function() {
//1、实现瀑布流布局
waterFall('main','box');
})
})
3、JS代码详解----父盒子居中
在css代码中我们并没有让父盒子居中,是因为
父盒子宽度是随着浏览器宽度的改变而改变的
。因此在 waterFall() 实现瀑布流函数中我们首先要做的就是使父盒子居中。
这里提一些大家需要注意的地方
(1)获取文档的宽度时推荐写兼容写法
(大噶可以参考:Scroll家族(写法类似))
(2)boxWidth 是每一个子盒子的宽度,因为瀑布流布局的特点是图片的宽度相同的,高度不同
,所以每一个子盒子的宽度都是相同的。
(3)设置父盒子的宽度,其宽度即子盒子所占列数 * 子盒子的宽度
。
(4)设置父盒子 margin = '0 auto’ 达到父盒子居中的效果
注释部分需要大噶特别注意
//1、根据图片的列数来确定父盒子的宽度,父盒子居中
//1.1 获取标签,父盒子和所有子盒子
var father= document.getElementById(parent);
var allBox = document.getElementsByClassName(child);
//1.2 获取其中一个的宽度
var boxWidth = allBox[0].offsetWidth;
//1.3 获取文档的宽度(兼容)
var screen = document.documentElement.clientWidth || document.body.clientWidth;
//1.4 求出当前图片的列数,是变化的
var cols = parseInt(screen / boxWidth);
//1.5 父盒子居中,给父盒子设置宽度
father.style.width = cols * boxWidth + 'px';
father.style.margin = '0 auto';
4、JS代码详解----子盒子定位
这里提一些大家需要注意的地方
(1)根据瀑布流布局的原理,我们需要把每一列子盒子的高度存放在一个数组中,即 heightArr,将新的子盒子每次追加在最小高度的子盒子的后面
。这一步骤使用 for循环* 和 if判断 语句就可以实现
(2)函数 minBox(box) 可以传递一个数组类型的参数,作用是找到数组中的最小值
,并且特别注意返回的是最小值的下标
(3)设置新的子盒子的定位,首先要设置 position = ‘absolute’; (子绝父相)
。其 left 即为 下标值 * 子盒子宽度
,top 值为其所跟子盒子的高度。
(4)更新高度作为该一列的高度。
注释部分需要大噶特别注意
//2、子盒子定位(从第二行开始)
//2.1 定义变量
var heightArr = [], boxHeight = 0, minBoxHeight = 0, minIndex = 0;
//2.2 遍历所有的盒子
for(let i = 0;i < allBox.length; i++) {
boxHeight = allBox[i].offsetHeight;
//2.3 判断是否是第一行
if(i < cols) {
heightArr.push(boxHeight)
}else { //剩余行做定位
//2.4 取出数组中最矮盒子的高度
minBoxHeight = heightArr[minBox(heightArr)];
//2.5 取出最矮盒子再数组中的索引
minIndex = minBox(heightArr);
//2.6 剩余子盒子的定位
allBox[i].style.position = 'absolute';
allBox[i].style.left = minIndex * boxWidth + 'px';
allBox[i].style.top = minBoxHeight + 'px';
//2.7 更新高度
heightArr[minIndex] += boxHeight;
}
}
function minBox(box) {
var j = 0;
for(i in box) {
if(box[j] > box[i])
j = i
}
return j;
}
七、升级版
上面已经实现了瀑布流布局,但发现浏览器中16张图片展示完后不会再有图片展示。在这里将在以上代码的基础上用Dock数据加载解决这个问题(实际中用ajax)
1、入口函数更改
在入口函数中 增加浏览器滚动的监听器 ,其内完成加载新的数据的功能
在实现瀑布流函数后增加自定义追加 check()函数 和 scroll(0兼容性函数
这里提一些大家需要注意的地方
(1)在浏览器滚动的监听器首先要使用 check()函数 检查是否需要增加 新数据
(2)在这里使用的是一个数组(实质是假数据)
实现新数据加载的功能
(3)遍历数组,创造新的节点追加到浏览器中
(4)追加新数据后重新进行瀑布流布局
window.addEventListener("load",function() {
//1、实现瀑布流布局
waterFall('main','box');
//2、加载数据(追加)
window.addEventListener('scroll',function() {
if(check()) {
//2.1 假数据
var dataArr = [
{"src":"../program1/images/1.jpg"},
{"src":"../program1/images/2.jpg"},
{"src":"../program1/images/3.png"},
{"src":"../program1/images/4.png"},
];
//2.2 遍历数据
for(var i = 0;i < dataArr.length; i++) {
var newBox = document.createElement('div');
newBox.className = 'box';
document.getElementById('main').appendChild(newBox);
var newPic = document.createElement('div');
newPic.className = 'pic';
newBox.appendChild(newPic);
var newImg = document.createElement('img');
newImg.src = dataArr[i].src;
newPic.appendChild(newImg);
}
//重新进行瀑布流布局
waterFall('main','box');
}
});
});
2、追加检查函数和scroll兼容性写法
(1)check()函数 为 true 的条件是 数组最后一个元素的高度 + 盒子高度的一半 <= 页面高度 + 页面滚出浏览器的高度(偏移高度)
(2)scroll()函数 的作用是得到页面滚出浏览器的高度的兼容性写法
(推荐参考:Scroll家族)
//追加在最矮盒子的后面
function check() {
//1、获取最后的盒子
var allBox = document.getElementsByClassName('box');
var lastBox = allBox[allBox.length - 1];
//2、 最后盒子高度的一半
var lastBoxDis = lastBox.offsetHeight*0.5 + lastBox.offsetTop;
//3、页面高度
var screenH = document.documentElement.clientHeight || document.body.clientHeight;
//4、求出页面滚出浏览器的高度(偏移高度)
var scrollTop = scroll().top;
//5、返回结构
return lastBoxDis <= screenH + scrollTop;
}
//兼容性
function scroll() {
if(window.pageYOffset != null){
//返回一个字面量对象(JSON对象,JSON中的键必须有双引号)
return {
"top":window.pageYOffset,
"left":window.pageXOffset
}
}else if (document.compatMode === 'CSS1Compat'){ //W3C
return {
"top":document.documentElement.scrollTop,
"left":document.documentElement.scrollLeft
}
}else {
return {
"top":document.body.scrollTop,
"left":document.body.scrollLeft
}
}
}
大噶若觉得这篇blog有帮助到你,走过路过点个👍呗
若有啥地方需要改进,也请大佬多多提点