详解瀑布流实现


瀑布流又称瀑布流式布局,是比较流行的一种网站页面布局方式,视觉表现为参差不齐的多栏布局。最早采用此布局的是网站是 Pinterest,后逐渐在国内流行,比如 花瓣, 蘑菇街等。

瀑布流布局效果

在这里插入图片描述

css flex布局实现方式

最外面是一个容器,设置display为flex,使容器内部div横向排列,形成列,div内部通过flex-column,纵向排列。

<div class="tab-container">
    <div class="column">
        <div class="tab-item">1</div>
        <div class="tab-item height50">2</div>
    </div>
    <div class="column">
        <div class="tab-item">3</div>
    </div>
    <div class="column">
        <div class="tab-item height50">4</div>
        <div class="tab-item">5</div>
        <div class="tab-item height50">6</div>
    </div>
</div>
* {
    margin: 0;
    padding: 0;
}
.tab-container {
    display: flex;
}
.column {
    flex-grow: 1;
    margin-right: 20px;
    display: flex;
    flex-direction: column;
}
.column:last-of-type {
    margin-right: 0;
}
.tab-item {
    height: 100px;
    background: white;
    margin-bottom: 10px;
}
.height50 {
    height: 50px;
}

样式结果如下图

在这里插入图片描述

优点:
能轻松实现瀑布流静态效果,且性能不错。

缺点:

  1. 在填充元素前,需要对数据进行分列处理。
  2. 还需要手工对已经分完列的数据进行适当的调整,以防止相邻列内容高度差距过大。
  3. 若是动态添加的元素,则还是需要进行第一步的处理,同时对第二步的处理就无能为力了。

css column属性实现方式

多行等宽元素排列,后面的元素依次添加到其后,等宽不等高,根据图片原比例缩放直至宽度达到我们的要求,依次放入。

html部分

<div class="tab-container">
    <div class="tab-item">tab1</div>
    <div class="tab-item height50">tab2</div>
    <div class="tab-item height50">tab3</div>
    <div class="tab-item">tab4</div>
    <div class="tab-item height50">tab5</div>
    <div class="tab-item">tab6</div>
    <div class="tab-item">tab7</div>
    <div class="tab-item">tab8</div>
    <div class="tab-item height50">tab9</div>
    <div class="tab-item">tab10</div>
</div>

css部分

* {
    margin: 0;
    padding: 0;
}
.tab-container {
  column-count: 3;
  column-gap: 30px;
}
.tab-container .tab-item{
  height: 150px;
  margin-bottom: 20px;
  break-inside: avoid;
  border: 1px solid #000;
  text-align: center;
  background: white;
}
.tab-container .height50 {
  height: 100px;
}

样式结果如下图
在这里插入图片描述
优劣: 能轻松实现瀑布流静态效果,且性能不错。缺点是后续动态添加的内容,会在最右侧依次添加,整体交互体验不符合实际需求。故需要js处理动态添加的内容。

那么js处理的规则是什么呢?

下面通过图解来分析一下瀑布流的算法。

图解瀑布流算法

当第一排排满足够多的等宽图片时(如下图情况),自然而然的考虑到之后放置的图片会往下面排放。
在这里插入图片描述
那么第六张图片,放置在什么位置呢?是下图的位置么?
在这里插入图片描述
我们通过瀑布流算法实验得到,后面紧跟的第六张图片的位置应该是这个位置。
在这里插入图片描述
为什么呢?
因为放置它之前,这一列的高度为所有列中最小,所以会放置在这个地方。
所以我们知道了,如果再继续放置下去,第七张图片应该是这个位置,对吗?
在这里插入图片描述

通过瀑布流算法实验得出位置正确。看懂这个图示应该就能理解了瀑布流的原理算法。
在这里插入图片描述

JS方式实现瀑布流

思路分析:

  1. 构建成行元素 + 寻找新增元素追加位置

    瀑布流所有元素的宽度是固定的,我们用浏览器的宽度除以每个瀑布流块的宽度,就是每一行可容纳的瀑布流块的个数。因为每个瀑布流块的高度不一,我们姑且把组成一行的这组元素称为成行元素,在成行元素放置完毕后,我们如果要再增加一个元素,那么它的位置应该这样找?

    获取成行元素集合中高度最低的那个元素,待放置的元素的top值应该是这个最低元素的高,left值应该是这个最低元素的left值。

    这样,新增的这一个元素我们就找到了它存放的位置,这样成行元素中的最低高度值就变为了原来的高度+新增元素的高度。

  2. 重复1,依赖成行元素追加新元素。

    1中我们已经实现了一次成行元素追加一个新的元素,这样新元素增加之后我们就构建了新的成行元素,之后的操作就是在新的成行元素中追加新元素,原理同1。

html部分

<div class="tab-container" id="tabContainer">
	<div class="tab-item">
		<img src="http://n.sinaimg.cn/photo/700/w1000h500/20210603/57a6-kracxep9657657.png" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/ent/4_img/upload/0b3147ad/294/w690h1204/20210905/0176-10a73c9a66bef6da4e151590480fb865.jpg" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/photo/700/w1000h500/20210603/57a6-kracxep9657657.png" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/ent/4_img/upload/0b3147ad/294/w690h1204/20210905/0176-10a73c9a66bef6da4e151590480fb865.jpg" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/ent/4_img/upload/0b3147ad/294/w690h1204/20210905/0176-10a73c9a66bef6da4e151590480fb865.jpg" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/photo/700/w1000h500/20210603/57a6-kracxep9657657.png" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/photo/700/w1000h500/20210603/57a6-kracxep9657657.png" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/ent/4_img/upload/0b3147ad/294/w690h1204/20210905/0176-10a73c9a66bef6da4e151590480fb865.jpg" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/photo/700/w1000h500/20210603/57a6-kracxep9657657.png" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/ent/4_img/upload/0b3147ad/294/w690h1204/20210905/0176-10a73c9a66bef6da4e151590480fb865.jpg" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/photo/700/w1000h500/20210603/57a6-kracxep9657657.png" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/ent/4_img/upload/0b3147ad/294/w690h1204/20210905/0176-10a73c9a66bef6da4e151590480fb865.jpg" />
    </div>
    <div class="tab-item">
		<img src="http://n.sinaimg.cn/photo/700/w1000h500/20210603/57a6-kracxep9657657.png" />
    </div>
</div>

css部分

* {
    margin: 0;
    padding: 0;
}
.tab-container {
    position: relative;
}
.tab-container .tab-item {
    position: absolute;
    height: auto;
    border: 1px solid #000;
    background: white;
    break-inside: avoid;
    text-align: center;
}
.tab-container .tab-item img {
    width: 100%;
    height: auto;
}

js部分

window.onload = function () {
    waterFall('#tabContainer', '.tab-item'); //实现瀑布流
}

/**
 * @param { string } wrapIdName 容器id(或class)名称
 * @param { string } contentIdName 容器中内容项id(或class)名称
 * @param { number } column 容器中内容展示列数 默认为4
 * @param { number } columnGap 容器中列间隔距离 默认为20
 * @param { number } rowGap 容器中行间隔距离 默认为20
 */
function waterFall(wrapIdName, contentIdName, columns = 4, columnGap = 20, rowGap = 20) {
    // 获得内容可用宽度(去除滚动条宽度)
    const wrapContentWidth = document.querySelector(wrapIdName).offsetWidth - 8;

    // 间隔空白区域
    const whiteArea = (columns - 1) * columnGap;

    // 得到每列宽度(也即每项内容宽度)
    const contentWidth = parseInt((wrapContentWidth - whiteArea) / columns); 

    // 得到内容项集合
    const contentList = document.querySelectorAll(contentIdName);

     // 成行内容项高度集合
    const lineConentHeightList = []

    for (let i = 0; i < contentList.length; i++) {
        // 动态设置内容项宽度
        contentList[i].style.width = contentWidth + 'px';

        // 获取内容项高度
        const height = contentList[i].clientHeight;

        if (i < columns) {
            // 第一行按序布局
            contentList[i].style.top = 0;
            contentList[i].style.left = contentWidth * i + columnGap * i + 'px';

            // 将行高push到数组
            lineConentHeightList.push(height)
        } else {
            // 其他行
            // 获取数组最小的高度 和 对应索引			
			let minHeight = Math.min(...lineConentHeightList)
            let index = lineConentHeightList.findIndex(listH => listH === minHeight)

            contentList[i].style.top = (minHeight + rowGap) + 'px';
            contentList[i].style.left = (contentWidth + columnGap) * index + 'px';
            
            // 修改最小列的高度 最小列的高度 = 当前自己的高度 + 拼接过来的高度 + 行间距
            lineConentHeightList[index] += height + rowGap
        }
    }
}

代码处理结果

在这里插入图片描述

  • 8
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

定栓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值