阿里面试官问:怎么实现一个瀑布流布局?
瀑布流布局
瀑布流布局是一种独具特色的网页布局形式。在这种布局中,页面上的元素如图片、卡片等会以参差不齐的方式排列,每行的长度并不相同,形成一种错落有致的视觉效果。当用户滚动页面时,新的元素会源源不断地动态加载,就像瀑布一样不断流淌,给人以流畅且富有吸引力的感受。它能够充分利用页面空间,灵活地展示大量内容,尤其适合用于图片分享、产品展示等网站,为用户带来独特的浏览体验。同时,这种布局方式也为网页设计带来了更多的创意和可能性。
阿里面试官:怎么实现一个瀑布流布局?
瀑布流布局的大体思路是这样的:
- 获取页面大小将页面划分为多个列。
- 每列中的元素依次排列,通过设置元素的浮动(通常为左浮动),使它们在同一列中依次排列。
- 实时监测每列的高度,并找出当前高度最小的列,将新元素添加到高度最小的列中。
实现步骤
html部分
<div id="container">
<div class="box">
<div class="box-img">
<img src="./img/1.webp" alt="">
</div>
</div>
.....
</div>
我们可以在类名为container的盒子中放入多个box类盒子,用来测试我们的瀑布流布局是否有问题。
css部分
* {
margin: 0;
padding: 0;
}
#container {
position: relative;
}
.box {
float: left;
padding: 5px;
}
.box-img {
width: 150px;
padding: 5px;
}
img {
width: 100%;
}
- 我们通过浮动让图片依次排列在一行里。
- 设置内边距,让图片之间隔开一些距离
- 固定每一个box的宽度,但是不固定高度。这样可以让图片等比列缩放,防止图片造成变形。
为什么我们要给#container 选择器的样式添加position属性,并且属性值为relative?
我们将在js部分解答这个问题。
JavaScript部分
我们通过js实现获取用户屏幕的宽度,决定一行能放下几张图;操作下一张图,放到上一行最矮的列下面。
我们可以通过封装在一个imgLocation()函数内实现这些功能。
imgLocation('container', 'box')
function imgLocation(parent, content) {}
-
获取id为container的元素
var cparent = document.getElementById(parent)
-
获取box数量
//获得box,方法一 var ccontainer = document.querySelectorAll('.box') //获得box,方法二 var ccontainer = ocument.querySelectorAll('#contain .box') //获取box,方法三(自己打造方法) var ccontent = getChildElement(cparent, content)
-
方法一:具有局限性,可能会获取到不是我们需要的box。
-
方法二:通过#contain .box可以获取到选择器为#contain 下的类名为.box 的元素,这样就不会获取到额外的box。但是这样并不灵活,如果我们不用box当作放置图片的盒子,我们就需要找到这个方法并且修改内容。
-
方法三:我们可以定义一个方法获取。
function getChildElement(parent, child) { //获取parent中所有的child var childArr = [] var allChild = parent.getElementsByTagName('*') //挑出来所有的box for (var i = 0; i < allChild.length; i++) { if (allChild[i].className == child) { childArr.push(allChild[i]) } } return childArr }
-
child== content == ‘box’,这样我们只需要修改imgLocation('container', 'box')里的值就好了。
imgLocation('container', 'box') function imgLocation(parent, content) { var cparent = document.getElementById(parent) var ccontent = getChildElement(cparent, content) } function getChildElement(parent, child) {}
-
创建一个childArr数组获取我们需要的box;设置allChild保存用getElementsByTagName()方法获取#contain标签中所有元素的集合。下图为allChild的内容(没有截全)。
我们通过遍历并且判断allChild[i].className == ‘box’,如果为true则通过push()方法加入childArr数组,否则继续遍历。
-
最后返回childArr数组。
-
-
-
动态获取页面一行可以放置几个box。
//获取每一个box的宽度 var imgWidth = ccontent[0].offsetWidth //用Math.floor()向下取整 var num = Math.floor(document.documentElement.clientWidth / imgWidth) //通过一行最多放置的box数动态设置#container标签的宽度 cparent.style.width = `${imgWidth * num}px`
num = (页面的宽度 / box的宽度)向下取整,num的值就是一行可以放下图片数量。
-
操作图片,放到上一行最矮的列下面。
var BoxHeightArr = [] for (var i = 0; i < ccontent.length; i++) { if (i < num) { //都是第一行的图 BoxHeightArr.push(ccontent[i].offsetHeight) } else { var minHeight = Math.min.apply(null, BoxHeightArr) var minIndex = BoxHeightArr.indexOf(minHeight) //摆放图片的位置 ccontent[i].style.position = 'absolute' ccontent[i].style.top = minHeight + "px" ccontent[i].style.left = ccontent[minIndex].offsetLeft + "px" //更新这一列的高度 BoxHeightArr[minIndex] = BoxHeightArr[minIndex] + ccontent[i].offsetHeight } }
创建BoxHeightArr数组保存每一列的高度。
从i=0,开始遍历到i < ccontent.length。
当i < num,就意味着都是可以排列在页面第一行,让BoxHeightArr数组保存其高度。
当i >= num,就意味着需要找到最矮的列后让该box补到最矮的那一列。
-
通过Math.min.apply(null, BoxHeightArr)获取数组最小高度。
-
通过BoxHeightArr.indexOf(minHeight)获取最小高度所在列。
-
在找到要添加的列后,摆放位置。
ccontent[i].style.position = 'absolute' //设置距离页面上边界的距离 ccontent[i].style.top = minHeight + "px" //设置距离页面左边界的距离 ccontent[i].style.left = ccontent[minIndex].offsetLeft + "px"
这里就可以知道为什么要给#container 选择器的样式添加position属性,并且属性值为relative。因为要给图片进行定位。
最后更新每列的高度。
-
小插曲——函数提升
imgLocation('container', 'box')
function imgLocation(parent, content) {}
因为代码是从上向下开始运行的。但是在运行到imgLocation()函数时,我们并没有定义它,而是在imgLocation()函数之后才定义。但是这样运行并没有报错。这是为什么呢?
是因为函数提升,它是指在代码执行之前,函数声明会被提升到当前作用域的顶部。这意味着在函数调用之前,即使函数定义在调用之后,也可以正常执行函数。
小结
我们现在了解了瀑布流布局是什么并且知道了如何实现。当面说被问到就不会慌张了。
转载自:https://juejin.cn/post/7361231869587226635