uni-app、H5实现瀑布流效果封装,列可以自定义


文章目录

  • 前言
  • 一、效果
  • 二、使用代码
  • 三、核心代码
  • 总结


前言

最近做项目需要实现uni-app、H5实现瀑布流效果封装,网上搜索有很多的例子,但是代码都是不够完整的,下面来封装一个uni-app、H5都能用的代码。在小程序中,一个个item渲染可能出现问题,也通过加锁来解决问题。


一、效果

1、下面看一下实现的效果,我这里的商品图片是正方形是固定大小的,如果你想要图片不同效果,也是可以适配的。
请添加图片描述

二、使用代码

1、下面是封装的组件如何使用

<TBodyrefresher:data="goodsList":is-end="isEnd":is-loading="isLoading":is-refreshing="isRefreshing"@refresh="reset"@lower="fetchGoodsNextPage"><TTMultiColumnListclass="bg-#fafafa goods"column-gap="16rpx":list="[]":column-size="2"@ready="updateColumnOperator"><template #default="{ data, index }"><viewclass="items_content">//这个是你的商品item,自己封装<TTGoodsCellPure:key="index":obj="data"arrangement="imageCenter"@click-item="onClickItem"/></view></template></TTMultiColumnList></TBody>

2、关键是updateColumnOperator方法,需要请求数据的时候把数据放进去渲染。

const goodsListQuery = {limit: 30,offset: undefined as string | undefined,
}
const isLoading = ref(false)
const goodsList = ref<Array<any>>([])
const isEnd = ref(false)
const isRefreshing = ref(false)// 获取商品列表
async function fetchGoodsList(options: { offset?: string; limit?: number } = {}) {const { offset, limit = goodsListQuery.limit } = options//接口自己替换自己的const { data } = await $apis.xxxxxx({categoryId: categoryId.value === -1 ? undefined : categoryId.value,keyword: '',offset,limit,})return { offset: data?.offset, list: data?.list ?? [] }
}// 获取商品列表
async function fetchGoodsPage() {if (isLoading.value || isEnd.value)returntry {goodsListQuery.offset = undefinedisLoading.value = trueconst { list, offset } = await fetchGoodsList({ offset: goodsListQuery.offset })if (list?.length) {goodsList.value = listif (list.length < goodsListQuery.limit)isEnd.value = true}else {isEnd.value = true}goodsListQuery.offset = offsetnextTick(() => {columnOperator?.reset(list)})}finally {isLoading.value = false}
}//下一页
async function fetchGoodsNextPage() {if (isLoading.value || isEnd.value)returntry {isLoading.value = trueisRefreshing.value = trueconst { list, offset } = await fetchGoodsList({ offset: goodsListQuery.offset })if (list?.length) {goodsList.value.push(...list)if (list.length < goodsListQuery.limit)isEnd.value = true}else {isEnd.value = true}goodsListQuery.offset = offsetcolumnOperator?.append(list)}finally {isRefreshing.value = falseisLoading.value = false}
}

三、核心代码

1、核心代码TTMultiColumnList代码

<script lang="ts" setup>
import type { Ref } from 'vue'
import { getCurrentInstance, nextTick, onMounted, ref } from 'vue'
import type { ColumnItem, ColumnOperator, ColumnOperatorPredictor, ListItem } from '@/utils/multiColumn'const props = withDefaults(defineProps<{list: Array<ListItem>columnSize: numbercolumnGap: stringrowGap: string}>(),{columnSize: 2,columnGap: 'normal',rowGap: 'normal',},
)const emit = defineEmits<{(e: 'ready', operator: ColumnOperator): void
}>()function range(count: number) {return Array.from({ length: count }, (_, i) => i)
}function getEmptyColumns(columnSize: number) {return range(columnSize).map(() => [])
}let appendColumnDataPromise = Promise.resolve(true)
const columns = ref<Array<Array<ColumnItem>>>(getEmptyColumns(props.columnSize))
const ctx = getCurrentInstance()
const columnRefs: Ref<Array<() => Promise<number>>> = computed(() => columns.value.map((_, i) => () => new Promise((resolve, reject) => {const className = `.s_${i}_ccList`// #ifdef H5const rect = document.querySelector(className)?.getBoundingClientRect()resolve(rect?.height || 0 as number)// #endif// #ifndef H5uni.createSelectorQuery().in(ctx).select(className).boundingClientRect().exec(([rect]) => {resolve(rect.height as number)})// #endif
})))// 获取高度最小一列的索引
async function getMinHeightColumnIndex(): Promise<number> {const columnHeights = await Promise.all(columnRefs.value.map(async (getHeight, index) => ({ height: await getHeight(), index })))return columnHeights.reduce((index, item, i) => {const height = columnHeights[index].heightconst siblingHeight = item.heightreturn siblingHeight < height ? i : index}, 0)
}// 将元素一个一个地插入到高度最小的一列
async function gradientAppendToColumn(startIndex: number, list: Array<ListItem>) {if (startIndex >= list.length)return falseconst targetColumnIndex = await getMinHeightColumnIndex()const item = { index: startIndex, data: list[startIndex] }const targetColumn = columns.value[targetColumnIndex]if (Array.isArray(targetColumn))targetColumn.push(item)else columns.value[targetColumnIndex] = [item]// render next itemreturn await new Promise((resolve) => {nextTick(async () => {// #ifndef H5// 解决小程序渲染问题await new Promise(resolve => nextTick(() => resolve(true)))// #endifawait gradientAppendToColumn(startIndex + 1, list)resolve(true)})})
}async function appendColumnDataInQueue(list: Array<ListItem>) {// 解决小程序渲染问题const oldAppendColumnDataPromise = appendColumnDataPromiseappendColumnDataPromise = new Promise((resolve) => {const cb = () => {appendColumnData(list).then(() => resolve(true)).catch(() => resolve(false))}oldAppendColumnDataPromise.then(() => cb()).catch(() => cb())})return appendColumnDataPromise
}async function appendColumnData(list: Array<ListItem>): Promise<boolean> {return await new Promise((resolve) => {nextTick(async () => {await gradientAppendToColumn(0, list)resolve(true)})})
}// 重置
async function resetColumnData(list?: Array<ListItem>): Promise<void> {if (list) {await appendColumnDataInQueue([])columns.value = getEmptyColumns(props.columnSize)await appendColumnDataInQueue(list)}
}// 移除元素
function removeColumnData(fn: (v: any) => boolean) {const staled = [] as Array<{ row: number; col: number }>columns.value.forEach((cols, colIndex) => {cols.forEach((d, rowIndex) => {if (fn(d.data))staled.push({ row: rowIndex, col: colIndex })})})staled.forEach(({ row, col }) => {columns.value[col].splice(row, 1)})
}// 更新元素
function updateColumnData(fn: ColumnOperatorPredictor, data: ListItem) {let done = falsefor (let col = 0; col < columns.value.length; col++) {if (done)breakconst rows = columns.value[col]for (let row = 0; row < rows.length; row++) {if (fn(rows[row].data)) {rows[row] = { index: rows[row].index, data }done = truebreak}}}
}onMounted(() => resetColumnData(props.list))emit('ready', {append: appendColumnDataInQueue,reset: resetColumnData,remove: removeColumnData,update: updateColumnData,
})
</script><template><view:style="{'display': 'grid','grid-template-columns': `repeat(${columns.length}, 1fr)`,'column-gap': props.columnGap,'row-gap': props.rowGap,'padding-left': '18rpx','padding-right': '18rpx','margin-top': '16rpx',}"><viewv-for="(rows, colIndex) in columns":key="colIndex"><view:key="`${colIndex}_list`":class="`s_${colIndex}_ccList`"><viewv-for="(row, rowIndex) in rows":key="`${colIndex}_${rowIndex}`"><slot:data="row.data":index="row.index":column-index="colIndex":row-index="rowIndex"/></view></view></view></view>
</template>

2、核心代码multiColumn代码

export type ListItem = unknownexport interface ColumnItem {index: numberdata: ListItem
}export type ColumnOperatorPredictor = (item: ListItem) => booleanexport interface ColumnOperator {readonly append: (list: Array<ListItem>) => voidreadonly remove: (predict: ColumnOperatorPredictor) => voidreadonly update: (predict: ColumnOperatorPredictor, data: ListItem) => voidreadonly reset: (list?: Array<ListItem>) => void
}

总结

这就是uni-app、H5实现瀑布流效果封装,希望能帮助到你,有什么问题可以私信给我。

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

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

相关文章

网络安全(秋招)如何拿到offer?(含面试题)

以下为网络安全各个方向涉及的面试题&#xff0c;星数越多代表问题出现的几率越大&#xff0c;祝各位都能找到满意的工作。 注&#xff1a;本套面试题&#xff0c;已整理成pdf文档&#xff0c;但内容还在持续更新中&#xff0c;因为无论如何都不可能覆盖所有的面试问题&#xf…

今天开始学习如何正式调查

本节要讲解三个内容 样本容量 调查方式 调查问卷的回收 在正式调查之前需要确定样本容量 就说要准备调查多少人确定好样本容量之后又要考虑设计的调查问卷 是以什么样的方式发出去 问卷的回收又要注意什么问题 要讲的主要内容 先看样本容量 样本容量确定的基本原…

redis+token+分布式锁确保接口的幂等性

目录 1.幂等性是什么&#xff1f; 2.如何实现幂等性呢&#xff1f; 1.新增管理员&#xff0c;出弹窗的同时&#xff0c;请求后台。 2.后端根据雪花算法生成唯一标识key&#xff0c;以雪花数为key存到redis。并返回key给前端。 3.前端保存后端传过来的key。 4.前端输入完成…

Navicat远程连接Linux的MySQL

打开Linux终端&#xff0c;进入root权限&#xff0c;用vim打开MySQL的配置文件 vim /etc/mysql/mysql.conf.d/mysqld.cnf将bind-address的值改为0.0.0.0 进入MySQL mysql -u root -p 将root用户改为允许远程登录 update user set host % where user root; 创建用户 CRE…

【C++奇遇记】函数探幽(上)

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集 数据库专栏 初阶数据结构 &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如…

玩转SWAT模型——最全面SWAT模型教程【建模方法、实例应用、高级进阶】

目录 第一部分&#xff1a;SWAT模型实践部分 第二部分&#xff1a;SWAT模型【进阶部分】 更多推荐 【专家】&#xff1a;刘老师【副教授】&#xff0c;北京重点高校资深专家&#xff0c;和美国SWAT软件开发方长期合作&#xff0c;拥有丰富的科研及工程技术经验&#xff0c;长…

node.js安装

下载 https://nodejs.org/en 安装 D:\Program Files\nodejs 配置 D:\Program Files\nodejs 目录下新建 node_cache 和 node_global 在cmd管理员身份运行&#xff1a; npm config set prefix "D:\Program Files\nodejs\node_global" npm config set cache &qu…

Netty 粘包半包

什么是 TCP 粘包半包&#xff1f; 假设客户端分别发送了两个数据包 D1 和 D2 给服务端&#xff0c;由于服务端一次读取到的字节 数是不确定的&#xff0c;故可能存在以下 4 种情况。 &#xff08;1&#xff09;服务端分两次读取到了两个独立的数据包&#xff0c;分别…

手机变电脑2023之虚拟电脑droidvm

手机这么大的内存&#xff0c;装个app来模拟linux&#xff0c;还是没问题的。 app 装好后&#xff0c;手指点几下确定按钮&#xff0c;等几分钟就能把linux桌面环境安装好。 不需要敲指令&#xff0c; 不需要对手机刷机&#xff0c; 不需要特殊权限&#xff0c; 不需要找驱…

怎么快速搭建BI?奥威BI系统做出了表率

搭建BI系统有两大关键&#xff0c;分别是环境搭建和数仓建设。这两点不管是哪一个都相当地费时费力&#xff0c;那要怎么才能快速搭建BI平台&#xff0c;顺利实现全企业数字化运营决策&#xff1f;奥威BI系统方案&#xff0c;你值得拥有&#xff01; 奥威BI系统方案&#xff0…

剑指Offer06. 从尾到头打印链表

# 06. 从尾到头打印链表 目录 # 06. 从尾到头打印链表题目代码&#xff08;双指针法&#xff09;代码逻辑 递归法代码设计流程 题目 官方地址 代码&#xff08;双指针法&#xff09; /*** Definition for singly-linked list.* public class ListNode {* int val;* L…

【计算机视觉 | Kaggle】飞机凝结轨迹识别 Baseline 分享和解读(含源代码)

文章目录 一、导读二、比赛背景三、比赛任务四、比赛数据五、评价指标六、Baseline6.1 Training part6.2 Submission part 一、导读 比赛名称&#xff1a;Google Research - Identify Contrails to Reduce Global Warming https://www.kaggle.com/competitions/google-researc…

【计算机网络】UDP协议

文章目录 1. UDP简介2. TCP和UDP的区别3. UDP的报文格式4. UDP的应用场景 1. UDP简介 UDP和TCP都是传输层协议&#xff0c;用于在计算机网络中发送和接收数据。数据可以是视频,可以是图片,可以是文字等.与TCP相比&#xff0c;UDP不提供数据传输的可靠性和流控制功能&#xff0…

8月16日起!亚马逊新商品上架需更新产品类型的274个属性!

亚马逊美国站发布公告称为了帮助买家更轻松地搜索产品&#xff0c;改善买家的购买决策提高卖家的销量&#xff0c;8月16日起受影响的200种产品类型的274个属性在上架前需更新属性&#xff0c;以下是公告内容&#xff1a; 自2023年8月16日起&#xff0c;200种产品类型的274个属…

18 | 基于DDD的微服务设计实例

为了更好地理解 DDD 的设计流程&#xff0c;这篇文章会用一个项目来带你了解 DDD 的战略设计和战术设计&#xff0c;走一遍从领域建模到微服务设计的全过程&#xff0c;一起掌握 DDD 的主要设计流程和关键点。 项目基本信息 项目的目标是实现在线请假和考勤管理。功能描述如下…

用于大型图像模型的 CNN 内核的最新内容

一、说明 由于OpenAI的ChatGPT的巨大成功引发了大语言模型的繁荣&#xff0c;许多人预见到大图像模型的下一个突破。在这个领域&#xff0c;可以提示视觉模型分析甚至生成图像和视频&#xff0c;其方式类似于我们目前提示 ChatGPT 的方式。 用于大型图像模型的最新深度学习方法…

轻松搭建酒店小程序

酒店小程序的制作并不需要编程经验&#xff0c;只需要按照以下步骤进行操作&#xff0c;就能很快地搭建自己的小程序商城。 第一步&#xff0c;注册登录账号进入操作后台&#xff0c;找到并点击【商城】中的【去管理】进入商城的后台管理页面&#xff0c;然后再点击【小程序商城…

vue实现文件下载

实现效果图&#xff1a;点击蓝色文字&#xff0c;下载文件 代码实现&#xff1a; <div v-for"(item, index) in form.fileList" :key"index"><i class"el-icon-upload" style"color: #c0c4cc; margin-right: 5px"></i&…

【嵌入式环境下linux内核及驱动学习笔记-(18)LCD驱动框架1-LCD控制原理】

目录 1、LCD显示系统介绍1.1 LCD显示基本原理1.1.1 颜色的显示原理&#xff1a;1.1.2 图像的构成 1.2 LCD接口介绍1.2.1 驱动接口 - MCU接口1.2.2 驱动接口 - RGB接口1.2.3 驱动接口 - LVDS接口1.2.4 驱动接口 - MIPI接口1.2.5 RGB / MIPI / LVDS三种接口方式的区别&#xff1a…

复习之selinux的管理

一、什么是selinux? SELinux&#xff0c;Security Enhanced Linux 的缩写&#xff0c;也就是安全强化的 Linux&#xff0c;是由美国国家安全局&#xff08;NSA&#xff09;联合其他安全机构&#xff08;比如 SCC 公司&#xff09;共同开发的&#xff0c;旨在增强传统 Linux 操…