仿照淘宝实现瀑布流

  • 实现的效果图如下(以iphone6/7/8以及ipadpro为例):iphone6/7/8ipadpro

实现思路:

  • 利用绝对定位进行布局
  • 定义gap(上下左右的间距)
  • 先计算出列数(columnNum)即网页的可视区域的宽度(clientWidth)/列宽(itemWidth),所以公式如下:
    columnNum=clientWidth/itemWidth
  • 将第一行每列的高度装进一个数组(columnHeightArr),用于高度定位
  • 通过循环,index<columnNum时,说明是第一行,同时将元素的高度装入columnHeightArr数组其他情况为非第一行
  • 找出columnHeightArr里面的最小值(minHeight),以及最小值对应的index,该列的下面就是下一个元素的位置,然后计算出距离左边,距离右边的距离,然后重置最小值的高度
  • 距离左边为:index*(item.offsetWidth+gap)
  • 距离上边边为:(元素对应的index)*(minHeight+gap)
  • 重置columnHeightArr数组对应的最小值为columnHeightArr[index]=item.offsetHeigth+gap+columnHeightArr[index]

    js示例代码如下:

    /* 瀑布流 
    parentEl:父级元素
    gap:上下左右的距离
    columFixNum:每行有多少列
    */
    
    class WaterFallFlow {
      constructor(parentEl, gap = 10, columFixNum = 2) {
          this.columFixNum = columFixNum
          this.parentEl = parentEl;//瀑布流布局对应的父节点
          this.childWidth = 0;//瀑布流布局对应的每个子节点的宽度
          this.gap = gap;//上下左右的间距
          /* 网页的可是区域 */
          this.clientWidth = 0
          this.coulmnNum = 0;//列数(可视区域的宽度/要瀑布流布局元素的宽度)
          this.columnHeightArr = [];//用于装列的高度
      }
      startWaterFall() {
          this.columnHeightArr = []//初始化以及窗口改变的时候重置第一列的高度
          this.clientWidth = document.documentElement.clientWidth || document.body.clientWidth || window.clientWidth
          this.childWidth = (this.clientWidth / this.columFixNum) - (this.gap / this.columFixNum)
          const parentEl = this.parentEl;//瀑布流布局对应的父节点
          const childWidth = this.childWidth;//瀑布流布局对应的每个子节点的宽度
          document.querySelector(parentEl).style.position = 'relative'//将父元素设置为相对定位
          const children = [...document.querySelector(parentEl).children];
          if (children.length == 0) throw new Error(`${parentEl}节点下未找到子节点`)
          children.forEach((item) => {
              item.style.position = 'absolute'//每一个元素设置为绝对定位
              item.style.width = `${childWidth}px`
          })
          const itemWidth = children[0].offsetWidth;
          this.coulmnNum = parseInt(this.clientWidth / itemWidth)//向下取整
          /* 第一行的布局 */
          children.forEach((item, index) => {
              /* 第一行 */
              if (index < this.coulmnNum) {
                  item.style.top = 0;
                  item.style.left = `${index * (item.offsetWidth + this.gap)}px`;
                  this.columnHeightArr.push(item.offsetHeight + item.offsetTop)
              } else {
                  // 不是第一行
                  const minHeight = Math.min(...this.columnHeightArr)//找到列中高度最小的哪一个元素,依次排在最小元素的后面
                  const minIndex = this.columnHeightArr.indexOf(minHeight)//找到最小高度的下标是哪一个
                  item.style.top = `${minHeight + this.gap}px`
                  item.style.left = `${minIndex * (item.offsetWidth + this.gap)}px`
                  this.columnHeightArr[minIndex] = minHeight + item.offsetHeight + this.gap//重置最小高度
              }
    
          });
      }
      init() {
          this.startWaterFall();
          window.addEventListener('resize', () => {
              setTimeout(() => {
                  this.startWaterFall()
              }, 300)
          })
      }
    }
    window.onload = function () {
      const waterfallflow = new WaterFallFlow('ul');
      waterfallflow.init()
    }
    

    htnl代码如下:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
      <title>Document</title>
      <style>
          * {
              padding: 0;
              margin: 0;
              list-style: none;
          }
    
    
          img {
    
              width: 100%;
    
          }
    
          body {
              background-color: #ccc;
          }
      </style>
    </head>
    
    <body>
      <div class="box">
          <ul>
              <li>
                  <img src="./img/1.png" alt="">
    
              </li>
              <li>
                  <img src="./img/2.png" alt="">
    
              </li>
              <li>
                  <img src="./img/3.png" alt="">
    
              </li>
              <li>
                  <img src="./img/4.png" alt="">
    
              </li>
              <li>
                  <img src="./img/5.png" alt="">
    
              </li>
              <li>
                  <img src="./img/6.png" alt="">
    
              </li>
              <li>
                  <img src="./img/7.png" alt="">
    
              </li>
              <li>
                  <img src="./img/8.png" alt="">
    
              </li>
              <li>
                  <img src="./img/9.png" alt="">
    
              </li>
              <li>
                  <img src="./img/10.png" alt="">
    
              </li>
              <li>
                  <img src="./img/11.png" alt="">
    
              </li>
              <li>
                  <img src="./img/12.png" alt="">
    
              </li>
          </ul>
      </div>
    </body>
    
    <!-- 瀑布流布局 -->
    <script src="./js/waterfallFlow.js"></script>
    
    </html>

GeGeZZ
6 声望0 粉丝