# 什么是瀑布流?

  • 瀑布流,又称瀑布流式布局
  • 是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部
  • 最早采用此布局的网站是 Pinterest,逐渐在国内流行开来。国内大多数清新站基本为这类风格
  • 即多行等宽元素排列,后面的元素依次添加到其后,等宽不等高,根据图片原比例缩放直至宽度达到我们的要求,依次按照规则放入指定位置。
  • 就比如下面这种效果就属于瀑布流布局

waterFall

# 实现思路

  • 本人比较懒,不做图介绍,这里进行文字描述~还有不明白的可自行问度娘!

  • 首先我们肯定是要先获取父元素与所有子元素

  • 然后获取第一个子元素的宽度和屏幕的宽度,然后使用 屏幕宽度 / 子元素宽度 向下取整计算出列数

  • 再声明一个数组存放元素的高度,数组长度应与列数一致

  • 数组中的第一组元素高度值应该是小于列数的元素高度值

  • 每次循环获取当前元素的高度值

  • 然后每次循环求出 最小高度与最小高度它的索引 ,以索引从小到大的顺序 依次 定位图片 top

  • 并利用 索引 * 图片宽度 计算出应在的列数位置

  • 同时利用最小索引求出应该相加的元素,将数组中的最小值进行累加

  • 举个栗子

// 这是一个元素高度值数组 此时最小值为 10 它的索引为 0
let list = [10, 20, 30, 40]
// 进行循环 例如当前元素高度为 50 那么数组中的最小值会加上元素高度 即 list [0] += 50 
let list = [10 + 50, 20, 30, 40] -> [60, 20, 30, 40] // 此时最小值为 20 它的索引为 1
// 接着循环第二个元素 假如高度值为 30 即应该 list [1] += 30
let list = [60, 20 + 30, 30, 40]  -> [60, 50, 30, 40] // 此时最小值为 30  它的索引为 2
// 依次类推....
  • 这就是实现瀑布流的思路,下面进行代码实现~

# 实现 waterFall

// 1.0 定义一个函数 接收 父元素 与 子元素们 选择器
function waterFall(parent, childs) {
  // 1.1 获取标签父元素与其中的所有子元素
  const parentEl = document.querySelector(parent)
  const childsEls = document.querySelectorAll(childs)
  // 1.2 获取一个子元素的宽度
  const childElWidth = childsEls[0].offsetWidth
  // 1.3 获取屏幕的宽度 进行兼容判断
  const screenWidth = document.documentElement.clientWidth || document.body.clientWidth
  
  // 1.4 根据上面条件计算出应排版列数
  const cols = ~~(screenWidth / childElWidth)
  // 1.5 给父元素宽度并进行居中
  parentEl.style.width = cols * childElWidth + 'px'
  parentEl.style.margin = '0 auto'
  // 2.0 声明数组 存放每次应该定位的高度 数组长度与列数一致
  const childsElHeightArr = []
  // 2.1 声明三个变量 分别用于保存 数组中最小高度、元素高度与最小索引值
  let minChildElHeight = 0, childElHeight = 0, minIndex = 0
  // 3.0 对所有子元素进行遍历
  childsEls.forEach((item, index) => {
    // 3.1 遍历获取元素的高度
    childElHeight = item.offsetHeight
    
    // 3.2 判断如果列数小于 index 则是第一排元素 添加进数组用于计算高度 
    if (cols > index)  return childsElHeightArr.push(childElHeight)
    // 3.3 否则利用算法金星定位
    // 3.4 计算出数组中的最小高度
    minChildElHeight = Math.min(...childsElHeightArr)
    
    // 3.5 计算数组最小高度所在的索引 这里我封装一个函数 calcMinIndex 即 -> 4.0 
    minIndex = calcMinIndex(childsElHeightArr)
    // 3.6 进行绝对定位
    item.style.position = 'absolute'
    // 3.7 利用最小索引 * 元素宽度 得出子元素据左的距离
    item.style.left = minIndex * childElWidth + 'px'
    
    // 3.8 将每次最小高度赋值给元素的高度
    item.style.top = minChildElHeight + 'px'
    // 3.9 将每次最小高度值与元素高度进行相加
    childsElHeightArr[minIndex] += childElHeight
    // 4.0 封装计算数组最小值的索引
    function calcMinIndex(array) {
      let index = 0
      for (let i = 0; i < array.length; i++){
        if (array[index] > array[i]) index = i
      }
      return index
    }
  })
}

# 测试代码

# CSS 代码

* {
    padding: 0;
    margin: 0;
  }
  img {
    max-width: 300px;
    vertical-align: top;
  }
  #main {
    position: relative;
  }
  .box {
    float: left;
    padding: 12px 0 0 12px;
  }
  .pic {
    padding: 10px;
    border: 1px solid #ccc;
  }

# HTML 代码

<div id="main">
  <div class="box">
    <div class="pic"><img src="./images/01.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/02.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/03.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/04.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/05.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/06.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/07.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/08.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/09.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/10.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/11.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/12.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/13.jpg" alt=""></div>
  </div>
  <div class="box">
    <div class="pic"><img src="./images/14.jpg" alt=""></div>
  </div>
</div>

# JavaScript 代码

window.addEventListener('load', () => waterFall('#main','.box'))
  • 实现效果图

waterFall

  • 至此,实现瀑布流篇章也告辞段落了,有时间还会分享一期结合瀑布流实现懒加载的 JavaScript 原生代码~