当前位置: 首页 > news >正文

【瀑布流大全】分析原理及实现方式(微信小程序和网页都适用)

前言

前端的同学在开发过程中,会遇到瀑布流这种布局方式,特别是商城类、社交类的项目,会很经常遇到。
先明确需求,再选择适合的方法,去渲染页面。

文章目录

  • 前言
    • 一、什么是瀑布流?
    • 二、实现方式: 通过 css 样式绘制瀑布流
        • 1.column-count 多列布局
        • 2. grid 网格布局 (网页版)
    • 三、 flex 分栏 + JS
        • 1.根据index奇偶分为两列
        • 2. 估算item的高度,将item放入更短的列
    • 四、scroll-view 结合 grid-builder

一、什么是瀑布流?

瀑布流 是一种非对称的纵向内容布局方式,核心特点是内容按列排列,每列高度随自身内容自动调整,新内容会不断加载到当前最短的列下方,视觉上像瀑布一样自然向下流动,无需传统分页操作。
这里举一个很经典的例子——小红书:
在这里插入图片描述
如图所示,从用户实际体验来看,它有两个最直观的感受:

  • “无限滑” 的浏览:不用点击 “下一页”,只要向下滚动屏幕,新内容就会自动加载出来,适合碎片化、沉浸式浏览(比如刷小红书笔记、淘宝商品、图片社区内容)。
  • 内容不被 “挤压”:图片、卡片等内容会按原始比例显示(比如竖版长图、方形图各展所长),不会被强行拉成统一尺寸,视觉上错落有致,重点更突出内容本身。

简单说,它就像把内容 “自然堆叠” 在多列里,列的高度随内容 “长”,用户下滑就能不断看到新内容,是现在很多内容 / 电商类平台常用的布局形式。

二、实现方式: 通过 css 样式绘制瀑布流

1.column-count 多列布局

利用 CSS 多列布局(column-count)自动将内容分配到指定列数,内容会按 “从上到下、从左到右” 的顺序填充,实现基础瀑布流效果。

核心原理:

  • column-count: N:指定布局为 N 列。
  • column-gap: Xpx:设置列之间的间距。
  • 子元素(如卡片、图片)会自动按顺序填充到各列,列高随内容自然撑开。

效果图:
在这里插入图片描述
可以直接复制在本地运行看一下效果哦~

<template><view class="demo-container"><view class="waterfall"><view class="item" v-for="item in items" :key="item.id" @click="navigateToDetail(item)"><image :src="item.image" mode="widthFix" /><text class="title">{{ item.title }}</text></view></view></view>
</template><script lang="ts" setup>
import { ref, onMounted } from 'vue'// Mock数据
const items = ref([{ id: 1, title: 'Item 1', image: 'https://picsum.photos/200/300' },{ id: 2, title: 'Item 2', image: 'https://picsum.photos/200/250' },{ id: 3, title: 'Item 3', image: 'https://picsum.photos/200/350' },{ id: 4, title: 'Item 4', image: 'https://picsum.photos/200/280' },{ id: 5, title: 'Item 5', image: 'https://picsum.photos/200/320' },{ id: 6, title: 'Item 6', image: 'https://picsum.photos/200/260' },{ id: 7, title: 'Item 7', image: 'https://picsum.photos/200/300' },{ id: 8, title: 'Item 8', image: 'https://picsum.photos/200/290' },
])onMounted(() => {// 可以在这里添加更多数据或进行其他初始化操作
})const navigateToDetail = (item: any) => {console.log('navigateToDetail', item)
}
</script><style lang="scss">
.demo-container {padding: 10px;
}.waterfall {column-count: 2;column-gap: 10px;
}.item {break-inside: avoid;margin-bottom: 10px;background: #fff;border-radius: 8px;overflow: hidden;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);image {width: 100%;display: block;}.title {padding: 8px;font-size: 14px;color: #333;}
}
</style>

特点:

  • 优点:实现简单,纯 CSS 无需 JS,自动适应列数,响应式友好。
  • 缺点:内容排列顺序是 “从上到下、从左到右”(先填满第一列再填第二列),而非 “优先填充最短列”,视觉上列高差异可能较大;动态加载新内容时,新内容会从最后一列继续填充,可能破坏布局平衡。
2. grid 网格布局 (网页版)

利用 CSS Grid 布局,结合 grid-template-rows: masonrymasonry 网格)实现 “优先填充最短列” 的瀑布流,更符合传统瀑布流的视觉效果(需注意浏览器兼容性)。

核心原理:

  • display: grid:启用网格布局。
  • grid-template-columns: repeat(N, 1fr):设置 N 列,每列宽度平均分配。
  • grid-template-rows: masonry:关键属性,启用 “masonry 布局”,让每行高度随内容动态调整,优先填充最短列(类似瀑布流)。

这里的图片是在小程序上写了展示的,大家看一下就行。
在这里插入图片描述
特点:

  • 优点:内容会优先填充最短列,列高更平衡,视觉效果更接近理想瀑布流;支持动态添加内容时自动平衡布局。
  • 缺点:grid-template-rows: masonry 是较新的 CSS 特性(2023 年起主流浏览器逐步支持),低版本浏览器(如 Safari 16 及以下)可能不兼容,需谨慎使用(可通过 caniuse 查看兼容性)。

三、 flex 分栏 + JS

1.根据index奇偶分为两列

这个就简单说一下,一般很少会用这样的方法,除非有特殊需求。
将列表分为两列,js中根据index,遍历出left列和right列。
在这里插入图片描述

// 样式代码
.waterfall-container {display: flex;padding: 16rpx;gap: 16rpx; /* 列间距 */
}.waterfall-column {flex: 1; /* 两列宽度相等 */
}// js方法
/*** 将数组按索引奇偶性分为两列* @param arr 源数组(可传入任意类型的数组)* @returns 包含左列(偶数索引)和右列(奇数索引)的对象*/
const splitArrayByIndex = () => {// 初始化左右两列数组let left = [];  // 存放偶数索引元素(index % 2 === 0)let right = []; // 存放奇数索引元素(index % 2 === 1)// 遍历数组,根据索引奇偶性分配元素arr.forEach((item, index) => {if (index % 2 === 0) {left.push(item);} else {right.push(item);}});return { left, right };
}

特点:

  • 优点:emm 简单?
  • 缺点:只能奇偶列出,不够灵活。
2. 估算item的高度,将item放入更短的列

直接来代码,样式都倒差不差。

// html 代码<view class="waterfall-layout"><view class="column"><YourCard v-for="card in list.left" :key="card.id" :item="card" /></view><view class="column"><YourCard v-for="card in list.left" :key="card.id" :item="card" /></view></view>// css代码
.waterfall-layout {width: fit-content;overflow-y: auto;display: grid;grid-template-columns: repeat(2, 1fr);gap: 12px;align-items: start;.column {width: fit-content;}}// js代码
const splitArrayByHeight = (arr: any[]) => {let leftHeight = 0let rightHeight = 0const left = []const right = []arr.forEach(item => {// 根据内容估算高度(需根据实际内容调整系数)const itemHeight = item.resource ? 320 : 160 // 假设有图片的卡片更高if (leftHeight <= rightHeight) {left.push(item)leftHeight += itemHeight} else {right.push(item)rightHeight += itemHeight}})return [left, right]
}

特点:

  • 优点:更加灵活,能满足大部分的需求场景;
  • 缺点:对于不同高度的图片,适应性没那么好,有待优化,优化之后估计会更好用一些。

四、scroll-view 结合 grid-builder

文档直通车:

  • 视图容器/scroll-view
  • Skyline /grid-builder
    在这里插入图片描述
    在这里插入图片描述
<template><view class="demo-container"><scroll-view class="scroll-container"scroll-y="true"@scrolltolower="loadMore"><view class="waterfall-container"><view class="column" v-for="(column, columnIndex) in columns" :key="columnIndex"><view @click="navigateToDetail(item)" class="item" v-for="(item, index) in column":key="index":style="{ height: item.height + 'px' }"><image :src="item.image" mode="widthFix" /><text class="title">{{ item.title }}</text></view></view></view></scroll-view></view>
</template><script lang="ts" setup>
import { ref, onMounted } from 'vue'// Mock数据生成函数
const generateMockData = (count: number) => {return Array.from({ length: count }, (_, i) => ({id: i,title: `Item ${i}`,image: `https://picsum.photos/200/${200 + Math.floor(Math.random() * 100)}`,height: 200 + Math.floor(Math.random() * 100)}))
}const columns = ref([[], []]) // 两列瀑布流
const loading = ref(false)// 分配数据到列
const distributeItems = (items: any[]) => {items.forEach(item => {// 找到当前高度较小的列const minHeightColumn = columns.value.reduce((prev, curr, index) => {const prevHeight = prev.reduce((sum, item) => sum + item.height, 0)const currHeight = curr.reduce((sum, item) => sum + item.height, 0)return currHeight < prevHeight ? curr : prev})minHeightColumn.push(item)})
}const navigateToDetail = (item: any) => {console.log('navigateToDetail', item)
}// 加载更多数据
const loadMore = () => {if (loading.value) returnloading.value = truesetTimeout(() => {const newData = generateMockData(10)distributeItems(newData)loading.value = false}, 1000)
}onMounted(() => {// 初始加载数据const initialData = generateMockData(20)distributeItems(initialData)
})
</script><style lang="scss">
.demo-container {width: 100%;height: 100vh;
}.scroll-container {height: 100%;
}.waterfall-container {display: flex;padding: 10px;gap: 10px;
}.column {flex: 1;display: flex;flex-direction: column;gap: 10px;
}.item {background: #fff;border-radius: 8px;overflow: hidden;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);image {width: 100%;display: block;}.title {padding: 8px;font-size: 14px;color: #333;}
}
</style>

特点:

  • 优点:组件很直接就能使用;
  • 缺点:可能不太稳定,具体看微信开发者社区,有些同学使用起来会出现没有正确渲染的问题。

总结
大家选择其中一个适合自己需求的来写就行,有更好的方法请分享!!!
是真的想学!!!! 觉得博主写的还不错,关注一个哇!!会持续输出好文章的!!

http://www.dtcms.com/a/511754.html

相关文章:

  • wordpress网站的常规安全设置经验分享
  • 代码随想录Day53|110. 字符串接龙、105.有向图的完全联通、106. 岛屿的周长
  • 做婚恋网站这几年做哪个网站致富
  • 【案例实战】听歌学英语鸿蒙APP从零到上架全流程回顾
  • 基于频域的数字盲水印blind-watermark
  • 三、网站开发使用软件环境中小企业建站的方法
  • 开源 Linux 服务器与中间件(八)数据库--MariaDB
  • Mac OS 安装 VirtualBox
  • wordpress卡密系统源码主题站长工具查询seo
  • 宁波快速建站公司附近的装修公司地点
  • 物联网运维中的自适应网络拓扑重构技术
  • jenkins介绍与部署
  • Attention:MHA->MQA->GQA->MLA
  • 拥塞控制原理
  • Flink Kafka 生产者原理与实现
  • 路由器和机顶盒的射频核心:深入解析PA、LNA、PHY与滤波器
  • Java----set
  • python编程网站推荐郑州云帆网站设计
  • 如何做论文网站给我一个用c 做的网站
  • 青岛网站排名公司自己的网站如何让百度收录
  • MQTT主题架构的艺术:从字符串拼接走向设计模式
  • i.MAX6ULL Linux LED 字符设备驱动代码分析
  • Linux中基数树的初始化
  • 4.3 二维数组
  • 【C语言实战(40)】C语言查找算法:从基础到实战的效率进阶
  • 洛谷 P2949 [USACO09OPEN] Work Scheduling G
  • 建站公司杭州南宁制作网站服务商
  • Deepseek-ocr论文精读
  • 【完整源码+数据集+部署教程】【文件&发票】发票信息提取系统源码&数据集全套:改进yolo11-ContextGuided
  • SpringBoot+Shiro+mybatis教务管理系统源码