鸿蒙OSUniApp 制作动态加载的瀑布流布局#三方框架 #Uniapp

使用 UniApp 制作动态加载的瀑布流布局

前言

最近在开发一个小程序项目时,遇到了需要实现瀑布流布局的需求。众所周知,瀑布流布局在展示不规则尺寸内容(如图片、商品卡片等)时非常美观和实用。但在实际开发过程中,我发现普通的 flex 或 grid 布局很难满足这一需求,尤其是当需要配合上拉加载更多功能时,更是增加了实现难度。

经过一番摸索和实践,我总结出了一套在 UniApp 中实现动态加载瀑布流布局的方案,希望能给大家提供一些参考和帮助。

瀑布流布局原理

瀑布流布局的核心思想是:将元素按照自上而下的方式依次排列,但不是简单地一列一列排,而是始终将新元素放置在当前高度最小的那一列,这样就能保证各列高度尽可能接近,整体视觉效果更加协调。

技术实现思路

在 UniApp 中实现瀑布流布局,我尝试过以下几种方案:

  1. 使用原生 CSS 的 column 属性
  2. 使用 flex 布局模拟
  3. 使用 JavaScript 计算每个元素的位置

最终我选择了第三种方案,因为它具有最好的兼容性和最大的灵活性,特别是在处理动态加载数据时。

具体实现步骤

1. 页面结构设计

首先,我们需要创建一个基本的页面结构:

<template><view class="waterfall-container"><view class="waterfall-column" v-for="(column, columnIndex) in columns" :key="columnIndex"><view class="waterfall-item" v-for="(item, itemIndex) in column" :key="item.id"@click="handleItemClick(item)"><image class="item-image" :src="item.imageUrl" :style="{ height: item.height + 'rpx' }"mode="widthFix"@load="onImageLoad(item, columnIndex, itemIndex)"/><view class="item-content"><text class="item-title">{{ item.title }}</text><view class="item-info"><text class="item-price">¥{{ item.price }}</text><text class="item-likes">{{ item.likes }}赞</text></view></view></view></view></view>
</template>

2. 数据结构和状态管理

<script>
export default {data() {return {columns: [[], []], // 默认两列瀑布流columnHeights: [0, 0], // 记录每列的当前高度page: 1,loading: false,hasMore: true,dataList: []};},onLoad() {this.loadInitialData();},// 上拉加载更多onReachBottom() {if (this.hasMore && !this.loading) {this.loadMoreData();}},methods: {async loadInitialData() {this.loading = true;try {const result = await this.fetchData(1);this.dataList = result.data;this.arrangeItems(this.dataList);this.hasMore = result.hasMore;this.page = 1;} catch (error) {console.error('加载数据失败:', error);uni.showToast({title: '加载失败,请重试',icon: 'none'});} finally {this.loading = false;}},async loadMoreData() {if (this.loading) return;this.loading = true;uni.showLoading({title: '加载中...'});try {const nextPage = this.page + 1;const result = await this.fetchData(nextPage);if (result.data && result.data.length > 0) {this.dataList = [...this.dataList, ...result.data];this.arrangeItems(result.data);this.page = nextPage;this.hasMore = result.hasMore;} else {this.hasMore = false;}} catch (error) {console.error('加载更多数据失败:', error);uni.showToast({title: '加载失败,请重试',icon: 'none'});} finally {this.loading = false;uni.hideLoading();}},// 模拟从服务器获取数据fetchData(page) {return new Promise((resolve) => {setTimeout(() => {// 模拟数据,实际项目中应该从服务器获取const mockData = Array.from({ length: 10 }, (_, i) => ({id: page * 100 + i,title: `商品${page * 100 + i}`,price: Math.floor(Math.random() * 1000 + 100),likes: Math.floor(Math.random() * 1000),imageUrl: `https://picsum.photos/200/300?random=${page * 100 + i}`,height: Math.floor(Math.random() * 200 + 200) // 随机高度,让瀑布流效果更明显}));resolve({data: mockData,hasMore: page < 5 // 模拟只有5页数据});}, 1000);});},// 核心算法:将新项目添加到高度最小的列中arrangeItems(items) {items.forEach(item => {// 找出当前高度最小的列const minHeightIndex = this.columnHeights.indexOf(Math.min(...this.columnHeights));// 将项目添加到该列this.columns[minHeightIndex].push(item);// 更新该列的高度(这里用item.height加上内容区域的估计高度)this.columnHeights[minHeightIndex] += (item.height + 120); // 120是内容区域的估计高度});},// 图片加载完成后,调整列高度计算onImageLoad(item, columnIndex, itemIndex) {// 这里可以根据实际加载后的图片高度重新计算列高度// 真实项目中可能需要获取图片实际渲染高度console.log('图片加载完成', item.id);},handleItemClick(item) {uni.navigateTo({url: `/pages/detail/detail?id=${item.id}`});}}
};
</script>

3. 样式设计

<style lang="scss">
.waterfall-container {display: flex;padding: 20rpx;box-sizing: border-box;background-color: #f5f5f5;
}.waterfall-column {flex: 1;display: flex;flex-direction: column;&:first-child {margin-right: 10rpx;}&:last-child {margin-left: 10rpx;}
}.waterfall-item {background-color: #ffffff;border-radius: 12rpx;margin-bottom: 20rpx;overflow: hidden;box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);transition: transform 0.3s;&:active {transform: scale(0.98);}
}.item-image {width: 100%;display: block;
}.item-content {padding: 16rpx;
}.item-title {font-size: 28rpx;color: #333;margin-bottom: 12rpx;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 2;overflow: hidden;text-overflow: ellipsis;
}.item-info {display: flex;justify-content: space-between;align-items: center;
}.item-price {font-size: 32rpx;color: #ff4b2b;font-weight: bold;
}.item-likes {font-size: 24rpx;color: #999;
}
</style>

优化与进阶

1. 图片懒加载

为了优化性能,特别是在图片较多的情况下,我们可以实现图片的懒加载功能。UniApp提供了lazyLoad属性,配合scrollview可以很容易实现:

<image class="item-image" :src="item.imageUrl" :style="{ height: item.height + 'rpx' }"mode="widthFix"lazy-load@load="onImageLoad(item, columnIndex, itemIndex)"
/>

2. 列数动态调整

针对不同屏幕尺寸,我们可以动态调整瀑布流的列数:

data() {return {columns: [],columnHeights: [],columnCount: 2, // 默认列数// 其他数据...};
},
onLoad() {// 获取设备信息,动态设置列数const systemInfo = uni.getSystemInfoSync();// 如果是平板等大屏设备,可以显示更多列if (systemInfo.windowWidth > 768) {this.columnCount = 3;}// 初始化列数据this.columns = Array.from({ length: this.columnCount }, () => []);this.columnHeights = Array(this.columnCount).fill(0);this.loadInitialData();
}

3. 下拉刷新

结合UniApp的下拉刷新功能,我们可以很容易实现列表刷新:

// 页面配置
export default {enablePullDownRefresh: true,// ...其他配置
}// 方法实现
onPullDownRefresh() {this.resetAndReload();
},resetAndReload() {// 重置数据this.columns = Array.from({ length: this.columnCount }, () => []);this.columnHeights = Array(this.columnCount).fill(0);this.page = 1;this.hasMore = true;this.dataList = [];// 重新加载this.loadInitialData().then(() => {uni.stopPullDownRefresh();});
}

实际应用案例

我在一个电商类小程序的商品列表页面应用了这种瀑布流布局,效果非常好。用户反馈说浏览商品时比传统的列表更加舒适,能够在同一屏幕内看到更多不同的商品,提升了浏览效率。

特别是对于衣服、家居用品等视觉信息很重要的商品,瀑布流布局可以根据商品图片的实际比例来展示,避免了固定比例裁剪可能带来的信息丢失,商品展示效果更佳。

遇到的问题与解决方案

1. 图片加载速度不一致导致布局跳动

问题:由于网络原因,图片加载速度可能不一致,导致已计算好位置的元素在图片加载后发生位置变化。

解决方案:预设图片高度,或者使用骨架屏占位,在图片完全加载后再显示真实内容。

<view class="waterfall-item"><view v-if="!item.imageLoaded" class="skeleton-image" :style="{ height: item.height + 'rpx' }"></view><image v-elseclass="item-image" :src="item.imageUrl" :style="{ height: item.height + 'rpx' }"mode="widthFix"/><!-- 内容部分 -->
</view>

2. 性能优化

在数据量很大时,可能会出现性能问题。我的解决方案是:

  1. 使用虚拟列表,只渲染可见区域的元素
  2. 分批次添加数据,而不是一次性添加所有数据
  3. 对于复杂计算,使用防抖和节流技术
// 分批次添加数据
arrangeItemsInBatches(items) {const batchSize = 5;const totalItems = items.length;let processedCount = 0;const processBatch = () => {const batch = items.slice(processedCount, processedCount + batchSize);this.arrangeItems(batch);processedCount += batch.length;if (processedCount < totalItems) {setTimeout(processBatch, 50);}};processBatch();
}

总结

通过在UniApp中实现动态加载的瀑布流布局,我们可以为用户提供更好的视觉体验和浏览效率。这种布局特别适合展示不规则尺寸的内容,如图片、商品卡片等。

实现这种布局的关键在于:

  1. 正确计算每列的高度并动态分配新元素
  2. 处理好图片加载和元素高度计算的问题
  3. 结合动态加载实现无限滚动效果
  4. 针对性能问题进行优化

希望这篇文章对你在UniApp中实现瀑布流布局有所帮助。如果有任何问题或建议,欢迎在评论区交流讨论!

参考资料

  1. UniApp官方文档:https://uniapp.dcloud.io/
  2. 瀑布流布局原理:https://www.w3cplus.com/css/pure-css-create-masonry-layout.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/905758.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ThinkStation图形工作站进入BIOS方法

首先视频线需要接在独立显卡上&#xff0c;重新开机&#xff0c;持续按F1&#xff0c;或者显示器出来lenovo的logo的时候按F1&#xff0c;这样就进到bios里了。联*想*坑&#xff0c;戴尔贵。靠。

【源码级开发】Qwen3接入MCP,企业级智能体开发实战!

Qwen3接入MCP智能体开发实战&#xff08;上&#xff09; 一、MCP技术与Qwen3原生MCP能力介绍 1.智能体开发核心技术—MCP 1.1 Function calling技术回顾 如何快速开发一款智能体应用&#xff0c;最关键的技术难点就在于如何让大模型高效稳定的接入一些外部工具。而在MCP技术…

Linux下载与安装

一、YUM 1.1 什么是YUM 在CentOS系统中&#xff0c;软件管理方式通常有三种方式&#xff1a;rpm安装、yum安装以及编译&#xff08;源码&#xff09;安装。 编译安装&#xff0c;从过程上来讲比较麻烦&#xff0c;包需要用户自行下载&#xff0c;下载的是源码包&#xff0c;需…

PostgreSQL中的全页写

一、概述 在PGSQL数据库中&#xff0c;默认的页面大小为8KB&#xff0c;但是磁盘buffer的大小为4KB&#xff0c;扇区大小为512B。这就导致在操作系统的角度看数据库的写操作&#xff0c;其实并不是一种原子操作。如果操作系统发生了系统级别的故障&#xff0c;此时正好操作系统…

WEB安全--Java安全--shiro550反序列化漏洞

一、前言 什么是shiro&#xff1f; shiro是一个Apache的Java安全框架 它的作用是什么&#xff1f; Apache Shiro 是一个强大且灵活的 Java 安全框架&#xff0c;用于处理身份验证、授权、密码管理以及会话管理等功能 二、shiro550反序列化原理 1、用户首次登录并勾选记住密码…

2024 睿抗机器人开发者大赛CAIP-编程技能赛-专科组(国赛)解题报告 | 珂学家

前言 题解 2024 睿抗机器人开发者大赛CAIP-编程技能赛-专科组&#xff08;国赛&#xff09;&#xff0c;陈越姐姐出题。 国赛比省赛&#xff0c;难度增强了不少&#xff0c;题目就剩下4个题了。 涉及堆栈&#xff0c;hash表&#xff0c;优先队列等高阶数据结构的使用&#x…

15 C 语言字符类型详解:转义字符、格式化输出、字符类型本质、ASCII 码编程实战、最值宏汇总

1 字符类型概述 在 C 语言中&#xff0c;字符类型 char 用于表示单个字符&#xff0c;例如一个数字、一个字母或一个符号。 char 类型的字面量是用单引号括起来的单个字符&#xff0c;例如 A、5 或 #。 当需要表示多个字符组成的序列时&#xff0c;就涉及到了字符串。在 C 语言…

操作系统-锁/内存/中断/IO

文章目录 锁自旋锁互斥锁悲观锁和乐观锁 内存管理物理/虚拟内存页表段表虚拟内存布局写时复制copy on writebrk&#xff0c;mmap页面置换算法 中断中断分类中断流程 网络I/OI/O模型服务器处理并发请求 锁 自旋锁 自旋锁是一种基于忙等待&#xff08;Busy-Waiting&#xff09;…

割点与其例题

割点 定义&#xff1a; 若一个点在图中被去掉后&#xff0c;图的连通块个数增加&#xff0c;那么这个点就被称为“割点”。如下图所示红点。 定义说白了就是若去掉一个点&#xff0c;图被“断开”的点称为割点。 朴素算法&#xff1a; 枚举每个点 u。遍历图&#xff0c;如果…

图卷积神经网络(Graph Convolutional Network, GCN)

最近看论文看到了图卷积神经网络的内容&#xff0c;之前整理过图神经网络的内容&#xff0c;这里再补充一下&#xff0c;方便以后查阅。 图卷积神经网络&#xff08;Graph Convolutional Network, GCN&#xff09; 图卷积神经网络1. 什么是图卷积神经网络&#xff08;GCN&#…

安装win11硬盘分区MBR还是GPT_装win11系统分区及安装教程

最近有网友问我,装win11系统分区有什么要求装win11系统硬盘分区用mbr还是GPT&#xff1f;我们知道现在的引导模式有uefi和legacy两种引导模式&#xff0c;如果采用的是uefi引导模式&#xff0c;分区类型对应的就是gpt分区(guid)&#xff0c;如果引导模式采用的是legacy&#xf…

服务培训QDA 的安装调试方法,硬件模块的讲解和软件控制台使用及系统测试

#服务培训##质谱仪##软件控制##硬件模块# 以下是关于Waters QDa单杆液质质谱仪的安装调试、硬件模块讲解以及软件控制台使用培训的相关内容&#xff1a; 安装调试 场地准备&#xff1a;用户需要提前准备好实验室&#xff0c;确保实验室环境符合仪器的要求&#xff0c;如温度、…

在K8S集群中部署EFK日志收集

目录 引言环境准备安装自定义资源部署ElasticsearchMaster 节点与 Data 节点的区别生产优化建议安装好以后测试ES是否正常部署Fluentd测试filebeat是否正常推送日志部署Kibana获取账号密码&#xff0c;账号是&#xff1a;elastic集群测试 引言 系统版本为 Centos7.9内核版本为…

polarctf-web-[rce1]

考点&#xff1a; (1)RCE(exec函数) (2)空格绕过 (3)执行函数(exec函数) (4)闭合(ping命令闭合) 题目来源&#xff1a;Polarctf-web-[rce1] 解题&#xff1a; 这段代码实现了一个简单的 Ping 测试工具&#xff0c;用户可以通过表单提交一个 IP 地址&#xff0c;服务器会执…

【串流VR手势】Pico 4 Ultra Enterprise 在 SteamVR 企业串流中无法识别手势的问题排查与解决过程(Pico4UE串流手势问题)

写在前面的话 此前&#xff08;用Pico 4U&#xff09;接入了MRTK3&#xff0c;现项目落地需要部署&#xff0c;发现串流场景中&#xff0c;Pico4UE的企业串流无法正常识别手势。&#xff08;一体机方式部署使用无问题&#xff09; 花了半小时解决&#xff0c;怕忘&#xff0c;…

ES(Elasticsearch)的应用与代码示例

Elasticsearch应用与代码示例技术文章大纲 一、引言 Elasticsearch在现代化应用中的核心作用典型应用场景分析&#xff08;日志分析/全文检索/数据聚合&#xff09; 二、环境准备(前提条件) Elasticsearch 8.x集群部署要点IK中文分词插件配置指南Ingest Attachment插件安装…

临床决策支持系统的提示工程优化路径深度解析

引言 随着人工智能技术在医疗领域的迅猛发展,临床决策支持系统(CDSS)正经历从传统规则引擎向智能提示工程的范式转变。在这一背景下,如何构建既符合循证医学原则又能适应个体化医疗需求的CDSS成为医学人工智能领域的核心挑战。本报告深入剖析了临床决策支持系统中提示工程的…

火山RTC 8 SDK集成进项目中

一、SDK 集成预备工作 1、SDK下载 https://www.volcengine.com/docs/6348/75707 2、解压后 3、放在自己项目中的位置 1&#xff09;、include 2&#xff09;、lib 3)、dll 暂时&#xff0c;只需要VolcEngineRTC.dll RTCFFmpeg.dll openh264-4.dll&#xff0c; 放在intLive2…

OkHttp用法-Java调用http服务

特点&#xff1a;高性能&#xff0c;支持异步请求&#xff0c;连接池优化 官方文档&#xff1a;提供快速入门指南和高级功能&#xff08;如拦截器、连接池&#xff09;的详细说明&#xff0c;GitHub仓库包含丰富示例。 社区资源&#xff1a;中文教程丰富&#xff0c;GitHub高…

python中常用的参数以及命名规范

以下是 Python 中常见的命名规范、参数用法及在大型项目中常用的操作模式&#xff0c;供记录参考&#xff1a; 1. 命名规范&#xff08;Naming Conventions&#xff09; 前缀/形式含义示例_age单下划线&#xff1a;弱“私有”标记&#xff08;可访问但不建议外部使用&#xff…