瀑布流布局原理及详解

一、原生 JS 实现瀑布流
  1. 瀑布流布局最大的特点就是等宽不等高
  2. 使用瀑布流布局可以使页面最后一行的容器高度差距最小,所以从第二行开始,每一个容器都要放在第一行容器高度最小的下方,以此类推
  3. 将所有容器(子元素)放在一个大容器(父元素)中,给父元素设置相对定位,子元素设置绝对定位,通过子元素的 top 和 left 属性值来调整其位置

页面的 DOM 元素

<div id="wrap">
  <div class="item1">1</div>
  <div class="item2">2</div>
  <div class="item3">3</div>
  <div class="item4">4</div>
  <div class="item5">5</div>
  <div class="item6">6</div>
  <div class="item7">7</div>
  <div class="item8">8</div>
  <div class="item9">9</div>
  <div class="item10">10</div>
</div>

css 样式

#wrap {
  width: 100%;
  height: calc(100vh - 20px);
  /* 父元素设置相对定位 */
  position: relative;
}
#wrap div {
  width: calc(100% / 3 - 20px / 3);
  background-color: #fff;
  border-radius: 8px;
  /* 子元素设置绝对定位 */
  position: absolute;
  /* 初始的左、上边距都设置为0 */
  left: 0;
  top: 0;
  font-size: 40px;
  font-weight: bold;
  text-align: center;
}
#wrap div img {
  width: 100%;
  border-radius: 8px 8px 0 0;
  vertical-align: bottom;
}
#wrap div p {
  font-size: 24px;
}
.item1 {
  height: 200px;
}
.item2 {
  height: 250px;
}
.item3 {
  height: 150px;
}
.item5,
.item7 {
  height: 200px;
}
.item4,
.item10 {
  height: 150px;
}
.item6 {
  height: 240px;
}
.item9 {
  height: 200px;
}
.item8 {
  height: 250px;
}

js 代码

// 通过CDN引入jQuery
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script>
let divList = $('#wrap div') // 获取到需要布局显示的div容器
divList = Array.from(divList) // 通过Array的from()方法将伪数组转化为真正的数组
let pageW = document.documentElement.clientWidth // 获取当前屏幕的宽度
let divW = divList[0].clientWidth // 获取一个div容器的宽度
let cols = Math.floor(pageW / divW) // 计算一行能够放几个div容器
let arrH = [] // 用来存储每个div容器的高度

// 循环div容器的数组
divList.forEach((item, index) => {
  // 1. 对下标小于一行个数的div容器进行操作
  if (index < cols) {
      arrH.push(item.clientHeight) // 将div容器的高度放入数组中
      item.style.left = index * divW + index * 10 + 'px' // 同时设置每个容器的left值,第一行top都为0
  }
})
// 2. 对剩余的div容器进行循环
for (let i = cols; i < divList.length; i++) {
  let minH = Math.min.apply(Math, arrH), // 获取到数组中最小的高度
      idx_min = arrH.indexOf(minH) // 查找到最小高度的下标
  divList[i].style.left = divList[idx_min].style.left // 让当前的div容器和最小下标的left值相同
  divList[i].style.top = minH + 10 + 'px' // top值在原来的最小高度上+10(为了是容器之间有一个间隙,可随意写)
  // 最小列的高度 = 当前自己的高度 + 新容器的高度 + 间隙
  arrH[idx_min] = minH + divList[i].clientHeight + 10
}
</script>

实现的结果图如下 

 图片效果 

二、column 多列布局实现瀑布流

主要使用了 column-count 和 column-gap 属性,column-count 属性用来设置列数,column-gap 属性用来设置列于列之间的间距。但是可以看到数字为 7 的模块被截断,图片没有出现截断的情况。

#wrap {
  width: 100%;
  height: calc(100vh - 20px);
  column-count: 3; /* 列数 */
  column-gap: 10px; /* 列与列之间的距离 */
}
#wrap div {
  background-color: #fff;
  border-radius: 8px;
  font-size: 40px;
  font-weight: bold;
  text-align: center;
  margin-bottom: 10px;
}
#wrap div img {
  width: 100%;
  border-radius: 8px 8px;
  vertical-align: bottom;
}
#wrap div p {
  font-size: 24px;
}
.item1 {
  height: 200px;
}
.item2 {
  height: 250px;
}
.item3,
.item5,
.item7 {
  height: 200px;
}
.item4,
.item10 {
  height: 150px;
}
.item6 {
  height: 240px;
}
.item9 {
  height: 100px;
}
.item8 {
  height: 250px;
}

实现效果如下 

 图片效果 

 column-count 对浏览器兼容性不是特别好,如下 

三、flex 属性实现瀑布流

将大容器(父元素)的 display 设置为 flex,然后使用 flex-flow 属性,设置为:flex-flow: column wrap。

flex-flow 作用是使弹性项目按列显示,并在需要时换行 flex-flow 属性是以下属性的简写属性:

  • flex-direction
  • flex-wrap css 样式
/* 去掉了大小容器的定位样式,将大容器设置为flex布局,并设置flex-flow属性 */
/*
    flex-flow属性是 flex-direction 和 flex-wrap 属性的简写形式
    flex-direction 控制元素的方向顺序
    flex-wrap 控制元素在必要时折行
*/
#wrap {
  width: 100%;
  height: calc(100vh - 20px);
  display: flex;
  flex-flow: column wrap;
}
#wrap div {
  width: calc(100% / 3 - 20px / 3);
  background-color: #fff;
  border-radius: 8px;
  font-size: 40px;
  font-weight: bold;
  text-align: center;
  margin-top: 10px;
  margin-right: calc(20px / 3);
}
#wrap div img {
  width: 100%;
  border-radius: 8px 8px 0 0;
  vertical-align: bottom;
}
#wrap div p {
  font-size: 24px;
}
.item1 {
  height: 200px;
}
.item2 {
  height: 250px;
}
.item3,
.item5,
.item7 {
  height: 200px;
}
.item4,
.item10 {
  height: 150px;
}
.item6 {
  height: 240px;
}
.item9 {
  height: 100px;
}
.item8 {
  height: 250px;
}

实现的结果图如下 

 图片效果 

 flex-flow 对浏览器的兼容性也不是特别好,但是相对 column-count 好一点,如下 

四、列数动态的瀑布流

第一种方式实现的是列数固定的布局,所以当列数可以任意指定时,上述方法就不再适用了,动态列数的 DOM 结构和样式和第一种方式是一样的,只有 js 代码略有不同。 js 代码

<script>
let divList = $('#wrap div')
divList = Array.from(divList) // 获取DOM节点,并转化为数组

let pageW = document.documentElement.clientWidth
let cols = 4 // 动态设置列数
let divW = parseInt((pageW - (cols + 1) * 10) / cols) // 计算每一个容器div的宽度并取整
let arrH = []

divList.forEach((item, index) => {
  if (index < cols) {
      arrH.push(item.clientHeight)
      item.style.left = index * divW + index * 10 + 'px'
  }
  item.style.width = divW + 'px' // 给每一个div容器设置宽度
})
for (let i = cols; i < divList.length; i++) {
  let minH = Math.min.apply(Math, arrH),
      idx_min = arrH.indexOf(minH)
  divList[i].style.left = divList[idx_min].style.left
  divList[i].style.top = minH + 10 + 'px'
  arrH[idx_min] = minH + divList[i].clientHeight + 10
}
</script>

实现的结果图如下 

 小结: 原生 js 的展示是以行展示,而 flex 布局和 column 布局是以列进行展示,可以根据项目需求进行选择

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值