瀑布流的三种实现方式

492 阅读3分钟

一、使用javascript原生实现

通过 js 计算每个元素的位置,然后使用 CSS 的绝对定位将它们放置在正确的位置。

1.1 实现思路

  1. 获取所有的子元素;
  2. 根据屏幕宽度和单个子元素的宽度,获得应该展示的列数,根据列数创建对应长度的数组;
  3. 遍历子元素,根据以下场景作区分:
    • 如果 i 小于列数,说明当前子元素应该在第一行,记录当前子元素的 offsetHeight 放在对应的数组位置上;
    • 否则,获取数组中最小的值的 index 并记录下来,设置当前子元素的 position 为 absolute,top 为 index 对应的值,left 为 index 对应的 offsetLeft,并将 index 对应的值改成 “当前子元素的 offsetHeight ➕ 之前的值“。

盒子之间的间距建议使用padding,因为需要使用offsetHeight计算盒子的高度,offsetHeight只包括padding,不包括margin

具体实现可查看下面代码👇

js代码实现:

window.onload = function () {
  waterfall('main', 'box');
  let dataInt = {
    data: [
      {src: '1.jpeg'},
      {src: '2.jpeg'},
      {src: '3.jpeg'}
    ]
  }
  window.onscroll = () => {
    if (checkScrollSlide()) {
      // 将数据块渲染到当前页面的尾部
      for (let i = 0; i < dataInt.data.length; i++) {
        let oParent = document.getElementById('main');
        let oBox = document.createElement('div');
        oBox.className = 'box';
        oParent.appendChild(oBox);
        let oPic = document.createElement('div');
        oPic.className = 'pic';
        oBox.appendChild(oPic);
        let oImg = document.createElement('img');
        oImg.src = `images/${dataInt.data[i].src}`;
        oPic.appendChild(oImg);
      }
      waterfall('main', 'box');
    }
  };
};

const waterfall = (parent, box) => {
  // 将main下的所有class为box的元素取出来
  let oParent = document.getElementById(parent);
  let oBoxs = getByClass(oParent, box);
  // 计算整个页面显示的列数(页面宽/box的宽)
  let oBoxW = oBoxs[0].offsetWidth;
  let cols = Math.floor(document.documentElement.clientWidth / oBoxW);
  // 设置main的宽
  oParent.style.cssText = 'width:' + oBoxW * cols + 'px; margin: 0 auto;';
  let hArr = []; // 存放每一列高度的数组
  for (let i = 0; i < oBoxs.length; i++) {
    if (i < cols) {
      hArr.push(oBoxs[i].offsetHeight);
    } else {
      let minH = Math.min.apply(null, hArr);
      let index = getMinHIndex(hArr, minH);
      oBoxs[i].style.position = 'absolute';
      oBoxs[i].style.top = minH + 'px';
      oBoxs[i].style.left = oBoxs[index].offsetLeft + 'px';
      hArr[index] += oBoxs[i].offsetHeight;
    }
  }
};

// 根据class获取元素
const getByClass = (parent, className) => {
  let boxArr = []; // 用来存储获取到的所有class为box的元素
  let oElements = parent.getElementsByTagName('*');
  for (let i = 0; i < oElements.length; i++) {
    if (oElements[i].className === className) {
      boxArr.push(oElements[i]);
    }
  }
  return boxArr;
};

const getMinHIndex = (arr, val) => {
  let min = arr.findIndex(i => i === val);
  return min;
};

//  检测是否具备了滚动条加载数据块的条件
const checkScrollSlide = () => {
  let oParent = document.getElementById('main');
  let oBoxs = getByClass(oParent, 'box');
  let lastBoxH = oBoxs[oBoxs.length - 1].offsetTop + Math.floor(oBoxs[oBoxs.length - 1].offsetHeight / 2);
  let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  let height = document.body.clientHeight || document.documentElement.clientHeight;
  return lastBoxH < scrollTop + height;
}

html代码实现:

<div id="main">
    <div class="box">
      <div class="pic">
        <img src="images/1.jpeg" />
      </div>
    </div>
    ...
  </div>

css代码实现:

body {
  margin: 0;
}

#main {
  position: relative;
}

.box {
  padding: 15px 0 0 15px;
  float: left;
}

.pic {
  padding: 10px;
  border: solid 1px #ccc;
  border-radius: 5px;
  box-shadow: 0 0 5px #ccc;
}

.pic img {
  width: 250px;
  height: auto;
}

1.2 优缺点

  • 优点:图片排序是按照图片计算的位置横向排列,位置是计算出来的,比较规范
  • 缺点:需要计算,列数 = 浏览器窗口宽度 / 子元素宽度,图片定位是根据每一列数据块的高度计算接下来图片的位置

二、css3多栏布局实现

2.1 使用column属性

通过 CSS 的列布局(column layout),将内容分为多列,使其呈现出瀑布流的效果。使用 column-countcolumn-gap 来设置列数和列之间的间隔。

css代码实现:

#main {
  /* 每一列宽度 */
  column-width: 202px;
  /* 最大列数 */
  column-count: 5;
  /* 列间隔 */
  column-gap: 5px;
}

2.1.1 优缺点

  • 优点:不需要计算,浏览器自动计算,只需设置列宽,性能高

  • 缺点:不可横向排列,只能纵向排列,打乱图片的排列顺序

2.2 使用flex布局

css代码实现:

#main {
  display: flex;
  flex-wrap: wrap;
  flex-direction: column;
  /* 容器必须有固定高度,且高度大于最高的列高 */
  height: 2000px;
  width: 500px;
  align-content: space-between;
}

2.2.1 优缺点

  • 优点:可实现横向排列的瀑布流
  • 缺点:容器必须有固定高度,并且高度要大于最高的列高

三、现成的库

Masonry.jsIsotope等一些开源的 JavaScript 库,专门用于实现瀑布流布局。使用这些库可以简化开发流程,提供更多的配置选项和动画效果。