阿里面试官——怎么实现一个瀑布流!
效果展示:
该如何来实现此种类型的瀑布流构局?粗看杂乱细看却有着章理的页面布局,想必爱刷小红书的你们会经历着上瘾的烦恼,正是这种布局有着相似的效果。
HTML
摆放页面前,要先为摆放的图片建一个 container,在 container这个容器中放入所要存放的箱子数量根据图片数来决定。
*{
margin: 0;
padding: 0;
}
根据要求选择文档中所有的元素,设置自行的元素边距。
#container{
position: relative;
}
.box{
float: left;
padding: 5px;
}
.box-img{
width: 150px;
padding: 5px;
border: 1px solid #10dc28;
}
img{
width: 100%;
}
-
#container
:- 给ID为 "container" 的元素设置了
position: relative;
,意味着该元素会相对于其包含块进行定位,这通常用于设置子元素的绝对定位时的参考坐标系。
- 给ID为 "container" 的元素设置了
-
.box
:- 定义了一个类为 "box" 的盒子元素样式。
- 使用了
float: left;
,使其在布局时向左浮动,允许其他内容(如果有的话)在其右侧排列。 - 设置了
padding: 5px;
,给盒子内部内容的四个方向都添加了5像素的填充。
-
.box-img
:- 定义了一个类为 "box-img" 的盒子元素样式,通常用于包含图像的盒子。
- 设定了
width: 150px;
,使该盒子的宽度固定为150像素。 - 添加了
padding: 5px;
,给盒子内部内容的四个方向都添加了5像素的填充。 - 添加了
border: 1px solid #10dc28;
,创建了一个1像素宽的绿色边框。
-
img
:- 定义了所有的图片元素的样式。
- 设置了
width: 100%;
,使图片的宽度始终填满其父元素的宽度,这是一种常见的响应式设计方法,使得图片能够根据其容器的大小进行自适应调整。
<div id="container">
<div class="box">
<div class="box-img">
<img src="./img/1.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/2.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/3.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/4.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/9.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/10.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/1.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/2.webp" alt="">
</div>
</div>
<div class="box">
<div class="box-img">
<img src="./img/10.webp" alt="">
</div>
</div>
<script src="./index.js"></script>
</div>
这里根据每个所需要的要求来存放N个箱子用以存纳。
CSS
现在面临的问题是我们要用户如何获取到用户屏幕的宽度,到底在一行中能存放多少张图片?紧接着,当第一行存满时,我们该如何操作下一章,放到上一行最矮的列下面?
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
}
有多少张图,该如何获取?不妨自己定义一个函数来自己解决获取图片问题
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
}
这个函数的作用是从指定的父元素中获取所有具有特定类名或标签名的子元素,并将它们存储在一个数组中返回。
具体来说,函数接受两个参数:parent
(父元素)和child
(子元素的类名或标签名)。
-
首先,函数创建了一个空数组
childArr
,用于存储匹配到的子元素。 -
然后,通过
parent.getElementsByTagName('*')
获取到指定父元素下的所有子元素,包括它的所有后代元素,存储在allChild
中。 -
接着,函数遍历
allChild
数组,检查每个子元素的类名或标签名是否与参数child
相匹配。如果匹配成功,则将该子元素添加到childArr
数组中。 -
最后,函数返回存储匹配到的子元素的数组
childArr
。
这个函数的主要目的是为了从父元素中提取出具有特定类名或标签名的子元素,以便在父元素的上下文中进一步处理或操作这些子元素。
imgLocation('container', 'box')
function imgLocation(parent, content) {
// 有多少张图
var cparent = document.getElementById(parent)
var ccontent = getChildElement(cparent, content) // document.querySelectorAll('#container .box')
// 拿出每一个box的宽度
var imgWidth = ccontent[0].offsetWidth
var num = Math.floor(document.documentElement.clientWidth / imgWidth)
cparent.style.width = `${imgWidth * num}px`
这个函数似乎是为了对指定容器中的图片进行布局,具体过程如下:
- 通过
document.getElementById(parent)
获取了指定 ID 为parent
的父容器元素。 - 通过调用之前定义的
getChildElement
函数,传递了父容器元素和内容元素的类名或标签名,以获取子元素集合ccontent
,这些子元素通常是包含图片的盒子。 - 从
ccontent
中取出第一个子元素,并获取其宽度,这里假设所有的盒子宽度相同,所以可以通过第一个盒子的宽度来代表所有的盒子宽度。这个宽度被用于计算一行能容纳多少张图片。 - 计算当前浏览器窗口的宽度,除以盒子宽度,得到一行能容纳的图片数量。
- 将父容器的宽度设置为一行图片总宽度,以便在一行中显示完整的图片,即
cparent.style.width = "${imgWidth * num}px"
。
这段代码的目的是让图片在水平方向上排列,使其能够在一行中按照一定的布局展示。
现在要解决的是,我们要操作的最矮的一张在哪? 操作之前需要获取到每一列的高度这样才能定位出最矮的那一张。
var BoxHeightArr = []
for(var i = 0;i < ccontent.length;i++){
if (i < num) { // 第一行
BoxHeightArr[i] = ccontent[i].offsetHeight
}else { // 要操作的
var minHeight = Math.min.apply(null,BoxHeightArr)
var minIndex = BoxHeightArr.indexOf(minHeight)
- 首先,创建了一个空数组
BoxHeightArr
,用于存储每个盒子元素的高度。 - 通过循环遍历
ccontent
数组,即所有的盒子元素,开始处理每个盒子的高度。 - 在循环中,首先判断当前盒子的索引
i
是否小于一行能容纳的图片数量num
,如果是,则表示当前盒子在第一行。对于第一行的盒子,将其高度存储在BoxHeightArr
数组的相应位置上。 - 如果当前盒子不在第一行,则表示需要调整布局,以保持布局的均衡性。这里通过比较
BoxHeightArr
数组中存储的每个盒子的高度,找到其中高度最小的盒子,并获取其索引minIndex
。
其次
// 摆放图片的位置
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
}
摆放好图片的高度,进行高度之间的更迭。
这段代码是用来摆放图片位置并更新列高度的部分:
-
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
:更新当前列的高度,将当前图片盒子的高度加到当前列的高度中,以便后续图片摆放时可以考虑当前列的新高度。
这段代码的作用是将图片按照瀑布流布局摆放在父容器中,确保图片在不同列中垂直居中对齐,并更新每一列的高度以便后续图片摆放。
基于此,便实现了图片的相继插放。
完整代码附上:
// 获取到用户屏幕的宽度,这就决定了一行能放下几张图
// 操作下一章,放到上一行最矮的列下面
imgLocation('container', 'box')
function imgLocation(parent, content) {
// 有多少张图
var cparent = document.getElementById(parent)
var ccontent = getChildElement(cparent, content) // document.querySelectorAll('#container .box')
// 拿出每一个box的宽度
var imgWidth = ccontent[0].offsetWidth
var num = Math.floor(document.documentElement.clientWidth / imgWidth)
cparent.style.width = `${imgWidth * num}px`
// 要操作的是哪一张,,每一列的高度都要知道
var BoxHeightArr = []
for(var i = 0;i < ccontent.length;i++){
if (i < num) { // 第一行
BoxHeightArr[i] = 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
}
}
console.log(minHeight);
}
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
}
进行代码的简略:
- 获取用户屏幕的宽度,并根据屏幕宽度确定一行能放下的图片数量
num
。 - 遍历所有的图片盒子元素,计算第一行各个图片盒子的高度,并将其存储在
BoxHeightArr
数组中。 - 对于超出第一行的图片盒子元素,找到当前高度最小的列(即
BoxHeightArr
数组中高度最小的值对应的索引minIndex
),将当前图片盒子元素放置在该列下方,实现瀑布流效果。 - 更新该列的高度,将当前图片的高度加到该列的高度中。
实现效果
希望我们在面对面试官时,遇到瀑布流能够侃侃而谈!
转载自:https://juejin.cn/post/7360917303088037942