10分钟纯 CSS 实现瀑布流

367 阅读6分钟

首先感谢大家对昨天 三个网站玩转 Grid 布局 文章的喜爱,文章的灵感来自前几日查看 StateOfCSS 时候,发现 Grid 布局的普及率已经相当高了,为了帮助自己复习,以及帮助大家去学习了解推荐了三个网站辅助学习。后续也将应评论区要求结合实战内容,对 Grid 布局更深入的聊一聊。

2020.stateofcss.com.png

在发现 Gird 布局普及的时候,也发现了一个比较冷门的 CSS 布局,Multi-Column 多列布局,大家对它的了解程度还不是很高,那让我们今天一起学习一下

image.png

初识 Multi-Column

CSS Multi-Column 多列布局是 CSS 的一个模块,它增加了对多列布局的支持。支持确定布局中的列数、内容应该如何从一列流动到另一列、列之间的间隙大小、列分隔线(称为列规则)以及它们的外观。

首先让我们一起看看这个模块特性的浏览器支持的怎么样,从www.caniuse.com/ 查询发现该特性已经支持了大部分浏览器。 image.png

说到多列布局,大家应该都很快的想到瀑布流吧。接下来让我们尝试用 Multi-Column 实现一下瀑布流。

遇见瀑布流

image.png

瀑布流实现原理

  1. 设置内容(图片)宽度一致
  2. 根据容器(浏览器)宽度以及每列宽度计算出列表个数
  3. 所有图片依次插入在当前高度最小的列下面
  4. 父容器高度取当前各列高度的最大值

先来看一下 Multi-Column 常用属性

column-count: 属性设置列的具体个数
column-width: 属性控制列的宽度
column-gap: 两列之间的缝隙间隔
column-rule: 规定列之间的宽度、样式和颜色
column-span: 规定元素应横跨多少列(n:指定跨n列  all:跨所有列)

主要有三个内容要设置

  • container 设置列数:column-count: 4;
  • container 设置列边距:column-gap: 10px;
  • item 设置内部具体内容
  • item 设置break-inside防止多列布局,分页媒体和多区域上下文中的意外中断

效果图如下

image.png

样例代码

HTML 源代码

<div class="container">
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
</div>

CSS 源代码

.container {
  column-count: 4;
  column-gap: 10px;
  padding-top: 10px;
}

.item {
  position: relative;
  margin-bottom: 20px;
  break-inside: avoid;
}

.item:nth-child(1) {
  height: 120px;
  background: #8092fc;
}
.item:nth-child(1)::after {
  content: "1";
  position: absolute;
  color: #fff;
  font-size: 16px;
  top: 50%;
  left: 50%;
}

.item:nth-child(2) {
  height: 263px;
  background: #886aaa;
}
.item:nth-child(2)::after {
  content: "2";
  position: absolute;
  color: #fff;
  font-size: 16px;
  top: 50%;
  left: 50%;
}

.item:nth-child(3) {
  height: 314px;
  background: #75d861;
}
.item:nth-child(3)::after {
  content: "3";
  position: absolute;
  color: #fff;
  font-size: 16px;
  top: 50%;
  left: 50%;
}

.item:nth-child(4) {
  height: 271px;
  background: #e5c71e;
}
.item:nth-child(4)::after {
  content: "4";
  position: absolute;
  color: #fff;
  font-size: 16px;
  top: 50%;
  left: 50%;
}

.item:nth-child(5) {
  height: 256px;
  background: #269d3a;
}
.item:nth-child(5)::after {
  content: "5";
  position: absolute;
  color: #fff;
  font-size: 16px;
  top: 50%;
  left: 50%;
}

.item:nth-child(6) {
  height: 154px;
  background: #88b6ae;
}
.item:nth-child(6)::after {
  content: "6";
  position: absolute;
  color: #fff;
  font-size: 16px;
  top: 50%;
  left: 50%;
}

.item:nth-child(7) {
  height: 290px;
  background: #a1b98a;
}
.item:nth-child(7)::after {
  content: "7";
  position: absolute;
  color: #fff;
  font-size: 16px;
  top: 50%;
  left: 50%;
}

.item:nth-child(8) {
  height: 341px;
  background: #773c7f;
}
.item:nth-child(8)::after {
  content: "8";
  position: absolute;
  color: #fff;
  font-size: 16px;
  top: 50%;
  left: 50%;
}

.item:nth-child(9) {
  height: 275px;
  background: #49f698;
}
.item:nth-child(9)::after {
  content: "9";
  position: absolute;
  color: #fff;
  font-size: 16px;
  top: 50%;
  left: 50%;
}

.item:nth-child(10) {
  height: 86px;
  background: #b804fc;
}
.item:nth-child(10)::after {
  content: "10";
  position: absolute;
  color: #fff;
  font-size: 16px;
  top: 50%;
  left: 50%;
} 

实践结果:纯css实现的瀑布流只能是一列一列的排布,根据列高动态设置 还是得用js来实现。

window.onload = function(){
    waterFlow("container", "box");
}
function waterFlow(parent, chirld){
    var wparent = document.getElementById(parent);//获取父级div, 最外级容器
    var allArr = getAllChirld(wparent,chirld);//获取到所有的class为box的容器div
    var wscreenWidth = document.documentElement.clientWidth;//获取屏幕宽度
    var wchirldWidth = wparent.getElementsByTagName("*");//获取所有的标签
    var num = Math.floor(wscreenWidth/wchirldWidth[0].offsetWidth);//这是一个Math算法, 目的是将小数转变为整数,
    // 从而可以知道每行最多容纳几张图片
    wparent.style.cssText = 'width:'+wchirldWidth[0].offsetWidth*num+'px;margin:0 auto';//固定每行摆放个数 和上下左右边距
    //获得每行的最小高度
    getMinHeightOfCols(allArr, num);
}
function getAllChirld(parent,classname){
    //获取所有的标签
    var wchirld = parent.getElementsByTagName("*");
    //创建数组
    var chirldArr = [];
    //遍历wchirld, 将其中className等于classname(传进来的参数)相同的标签放入数组chirldArr中
    for(var i = 0; i<wchirld.length; i++){
        if(wchirld[i].className==classname){
            //因为是位push所以没放进去一个, 都是在数组的最后一个
            chirldArr.push(wchirld[i]);
        }
    }
    //返回该数组
    return chirldArr;
}
function getMinHeightOfCols(chirdArr, num){
    //创建数组, 用来盛放每一行的高度
    var onlyOneColsArr = [];
    for(var i = 0; i<chirdArr.length; i++){

        if(i<num){
            //num为传进来的参数, 即为每行放图片的张数, 此步骤的目的是为了将第一行每张图片的高度遍历出来存放如新数组
            onlyOneColsArr[i]=chirdArr[i].offsetHeight;
        } else {
            //当大于每行存放的图片个数时进入该方法, Math.min.apply这个方法是为了得到数组中的最小值
            var minHeightOfCols = Math.min.apply(null, onlyOneColsArr);
            //此方法的目的是为了得到最小高度图片的下表, 也就是在每行的第几张, 具体方法见下面
            var minHeightOfindex = getminIndex(onlyOneColsArr, minHeightOfCols);
            //定义布局方式为绝对布局
            chirdArr[i].style.position = "absolute";
            //得到下一行图片应放的高度
            chirdArr[i].style.top = minHeightOfCols + "px";
            //得到下一行图片应放于那个位置
            chirdArr[i].style.left = chirdArr[minHeightOfindex].offsetLeft + "px";
            //将两张图片高度相加得到一个新的高度用来进行下一次的计算
            onlyOneColsArr[minHeightOfindex] += chirdArr[i].offsetHeight;
        }
    }

}
//此方法是为了进行最小高度下标的确定
function getminIndex(onlyOneColsArr, min){
    //遍历传进来的高度数组
        for(var i in onlyOneColsArr){
            //如果高度等于最小高度, 返回i即为该图片下标
            if(onlyOneColsArr[i] == min){
                return i;
            }
        }
}

总结

今天我们一起学习了 Multi-Column 多列布局,他在较为简单的场景可以代替 flex 和 grid ,使用较少的代码实现多列等宽布局。

❤️ 感谢大家

如果你觉得这篇内容对你挺有有帮助的话:

点赞支持下吧,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)

关注公众号咸鱼爱前端,我们一起学习一起进步。

觉得不错的话,也可以阅读其他文章(感谢朋友的鼓励与支持🌹🌹🌹):

三个网站玩转 Grid 布局

Nodejs 实现定时爬虫

React-Query 让你的状态管理更优雅

前端页面布局学习神器

面试必备知识点之深浅拷贝

SPA 前端路由原理与实现