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.hqwc.cn/news/54496.html

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

相关文章

redis缓存雪崩和缓存击穿

目录 缓存雪崩 解决方案&#xff1a; 缓存击穿 ​解决方案 缓存雪崩 缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力。 解决方案&#xff1a; u 给不同的 Key 的 TTL 添加随机值 u 利用 Redis …

【黑马头条之kafka及异步通知文章上下架】

本笔记内容为黑马头条项目的kafka及异步通知文章上下架部分 目录 一、kafka概述 二、kafka安装配置 三、kafka入门 四、kafka高可用设计 1、集群 2、备份机制(Replication&#xff09; 五、kafka生产者详解 1、发送类型 2、参数详解 六、kafka消费者详解 1、消费者…

C++派生类的构造函数

1.构造函数 定义了派生类之后&#xff0c;要使用派生类就需要声明该类的对象。对象在使用之前必须初始化。 派生类的成员对象是由所有基类的成员对象共同组成的。因此构造派生类函数的对象时&#xff0c;就要对基类的成员对象和新增的成员对象进行初始化。 基类的构造函数并…

Linux CentOS系统怎么下载软件

Linux CenOS系统想要下载软件可以在Linux内置的应用商店&#xff0c;并通过Yum 包管理器来下载&#xff08;直接使用yum命令下载软件&#xff09; 在Linux系统中&#xff0c;Yum&#xff08;Yellowdog Updater, Modified&#xff09;是用于管理RPM软件包的一个包管理器。 安装…

ChatGPT-4.0:你准备好了吗?

3202年了&#xff0c;你还在用ChatGPT 3.5吗&#xff1f; 来感受一下ChatGPT 4.0的魅力吧 文末附升级链接 1、颠倒黑白&#xff1f; 2、解读幽默&#xff1f; 3、小镇做题家&#xff1f; 如何白嫖&#xff1f; 最后 1、科技的发展加快了知识更新的速度&#xff0c;唯有终身…

数据库操作系列-Mysql, Postgres常用sql语句总结

文章目录 1.如果我想要写一句sql语句&#xff0c;实现 如果存在则更新&#xff0c;否则就插入新数据&#xff0c;如何解决&#xff1f;MySQL数据库实现方案: ON DUPLICATE KEY UPDATE写法 Postgres数据库实现方案:方案1&#xff1a;方案2&#xff1a;关于更新&#xff1a;如何实…

前端实现打印1 - 使用 iframe 实现 并 分页打印

目录 打印代码对话框预览打印预览 打印代码 <!-- 打印 --> <template><el-dialogtitle"打印":visible.sync"dialogVisible"width"50%"top"7vh"append-to-bodyclose"handleClose"><div ref"print…

大数据与okcc呼叫中心融合的几种方式

在实际的生产实践中&#xff0c;为提高营销效率&#xff0c;避免骚扰大众&#xff0c;很多呼叫中心业务会与大数据平台进行合作&#xff0c;进行精准营销。 买卖数据是非法的&#xff0c;大数据平台方并不会提供直接的数据&#xff0c;一般情况下&#xff0c;提供的数据都是脱…

C#利用自定义特性以及反射,来提大型项目的开发的效率

在大型项目的开发过程中&#xff0c;需要多人协同工作&#xff0c;来加速项目完成进度。 比如一个软件有100个form&#xff0c;分给100个人来写&#xff0c;每个人完成自己的Form.cs的编写之后&#xff0c;要在Mainform调用自己写的Form。 如果按照正常的Form form1 new For…

【数据分析入门】人工智能、数据分析和深度学习是什么关系?如何快速入门 Python Pandas?

目录 一、前言二、数据分析和深度学习的区别三、人工智能四、深度学习五、Pandas六、Pandas数据结构6.1 Series - 序列6.2 DataFrame - 数据框 七、输入、输出7.1 读取/写入CSV7.2 读取/写入Excel7.3 读取和写入 SQL 查询及数据库表 八、调用帮助九、选择(这里可以参考上一篇文…

【动态规划刷题 5】 最小路径和地下城游戏

最小路径和 链接: 64. 最小路径和 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 输入&#xff1a;grid [[1,3,1],[1,5,1],[4,2,1]] 输…

Qt--QPlugin插件

写在前面 Qt–动态链接库一文中提到&#xff0c;动态方式加载dll只能加载 extern "C“ 的导出函数&#xff0c;而无法加载类&#xff0c;因此可以使用Qt提供的插件来实现导出类的动态加载。 QPlugin是Qt插件框架的一部分&#xff0c;是一种轻量级的插件系统&#xff0c;…