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

实现思路:Vue 子组件高度不固定下实现瀑布流布局

实现思路:Vue 子组件高度不固定下实现瀑布流布局

在这里插入图片描述

一、瀑布流布局基础实现原理

在深入解说不定高度子组件的瀑布流如何实现之前,先大体说一下子组件高度固定已知的这种实现原理:

  1. 有一个已知组件高度的数组。
  2. 定义好这个瀑布流的列数,每列的宽度。
  3. 放置这些子组件的容器设置 position: relative 属性,内部子组件设置 position: absolute 属性,也就是说子组件可以在容器中以 left: --px; top: --px 的方式随意定位。
  4. 依次放置子组件,并记录离顶部最小距离的列数和位置值。下一个子组件的放置位置就是这里。
  5. 按照上面的的操作依次放置数组内所有元素到 dom。

二、我的需求

能看到上面瀑布流的实现前提,是需要每个子组件都有明确固定高度。
而我有一场景是:子组件的高度不能提前知道,它的高度由组件内部的文本多少来决定,它能显示多高就显示多高。
像这种,就需要在渲染过程中去判断最后一个合理的放置位置。

三、子组件动态高度的瀑布流,实现原理

搞了一整天,总算搞出来了,效果还可以。
这个渐进的过程是我添加了一个 timeout 实现的,实际可以更快的刷出来。

在这里插入图片描述


用一句话概括就是:
找到每列中最后可放置位置的 top 值,对比出最小的,作为下一个元素的放置位置。


Vue 实现瀑布流的问题是,Vue 是数据驱动的,就需要在渲染之前就知道每个组件的具体位置。而这,是无法一次性实现的,只能一一去把元素添加了待显示的数组中,当每个元素添加之后,再去计算下一个组件的放置位置。

说一下实现原理,知道原理之后,需要的只是如何实现它。

  1. 定义好你要显示多少列 colCountarrayOrigin 放置原始的数组, arrayShow 用于列表渲染,过程就是将 arrayOrigin 内的元素依次添加到 arrayShow 中,这个过程中去给每个元素添加 top left 位置值
  2. 第一行内部的展示不需要考虑高度值,因为都是 top: 0,放置的时候要标记自己是哪一列,后面会用到。
  3. 依次放置每个子组件到容器中,由于高度是不定的,需要到 nextTick 里面去放置下一个组件,这里可以通过递归的方式去放置,直到元素数量与要放置的元素数量一致。
  4. 后面的只需要查找容器里的最后 colCount + 1 个组件的位置,在每一列中找出每个子组件 offsetTop + offsetHeight 最小值的位置,并标记这个 col 列数,作为放置下一个组件的位置。
  5. 依次执行,直到放完。

在这里插入图片描述

取多少个子组件作为缓存合适?

按照上面的逻辑去实现之后,你会遇到一个新的问题:
在获取容器中最后几个子组件,并获取到每列距离 top 最小的值的时候,可能会略过某列。原因是这个 colCount + 1 的缓存区的数量太小。

像下面这张图一样,如果只取 colCount + 1 个元素的值去计算高度,那么就会忽略前面第二列的高度值。错误的放置在了红色位置。

在这里插入图片描述
原因就是在向后追溯最后 colCount + 1 个元素的时候,这个数量不足以覆盖所有列。如下图,至少需要向上找 13 个元素才可以。
所以我的这个页面中取了上 50 个。

在这里插入图片描述

四、完整代码

看源码吧,这是我在我一个开源项目《标题日记》中实现的一个功能。

github 页面源码: https://github.com/KyleBing/diary/blob/master/src/page/listHole/ListHole.vue
《标题日记》github: https://github.com/KyleBing/diary

主要的代码部分,不完整,完整的请看上面的源码

/*** 列表渲染*/
const diariesShow = ref<Array<DiaryEntityHole>>([])  // 列表展示的日记
const loadGap = 100 // 卡片加载间隔时长,单位 ms
const isShowLoadProcess = true // 是否显示卡片加载的过程const colCount = 10 // 列数
let lastDiaryIndex = 1  // 最后一个日记的 index
let lastTopPos = 0  // 最后一个日记的末尾位置: 距离 TOP
let lastCol = 0  // 下次该放置的 col index,哪一列
let colWidth = storeProject.insets.windowsWidth / colCount  // 每个元素的宽度const loadTimeOutHandle = ref()  // 载入过程的 timeOut handle
const isNeedLoadNextTimeout = true  // 是否要打断 timeout 的载入过程function renderingHoleList(newDiaries: Array<DiaryEntityDatabase>, index: number){// 如果不需要载入下面的内容,在 reload 的时候会遇到这种情况if (!isNeedLoadNextTimeout){return}// 1. 转成 DiaryEntityHole 对象let diary = newDiaries[index] as DiaryEntityHolediary.position = {top:  lastTopPos,left: lastCol * colWidth,col: lastCol}// 2. 添加到展示的列表中diariesShow.value.push(diary)nextTick(()=>{// 3. 待其渲染完成后再去处理下一个let domItems = Array.from((document.querySelector('.diary-list-hole') as HTMLDivElement).children) // Elements 转成数组// 3.1 第一排,前 colCount 个是不需要知道位置的,因为 top 都为 0if (lastDiaryIndex < colCount - 1){lastCol = lastCol + 1lastTopPos = 0}// 3.2 以后其它的else {// 取后 colCount 个元素的 lastTopPoslet countInDomItems = domItems.length > 50? domItems.slice(domItems.length - 50):domItemslet domItemsHeightColArray = countInDomItems.map(item => {let dom = item as HTMLDivElementlet col = Number(dom.getAttribute('data-col'))let posTop = dom.offsetTop + dom.offsetHeightreturn {posTop,col}})// Map 放置第 col 的最大高度值,这里用 Map 或 Set 都可以,反正就是为了使值唯一let everyColLastMaxPosMap = new Map()  // [2,345],[3,234],[4,456]domItemsHeightColArray.forEach(item => {// 获取已经存在的 lastPoslet existColPos = everyColLastMaxPosMap.get(item.col)if (existColPos === undefined){everyColLastMaxPosMap.set(item.col, item.posTop)} else {if (item.posTop >= existColPos){ // 如果有更大的,使用最大的everyColLastMaxPosMap.set(item.col, item.posTop)}}})// 将 Map 转成数组let everyColLastPosArray: Array<{posTop: number, col: number}> = []everyColLastMaxPosMap.forEach((value, key) => {everyColLastPosArray.push({posTop: value,col: key})})everyColLastPosArray.sort((a,b) => b.col - a.col) // 小值在前.sort((a,b) => a.posTop - b.posTop) // 大值在前lastTopPos = everyColLastPosArray[0].posToplastCol = everyColLastPosArray[0].col// console.log(`${lastDiaryIndex}: `, lastTopPos, lastCol,  everyColLastPosArray, domItemsHeightColArray)}// 4. index + 1index = index + 1lastDiaryIndex = lastDiaryIndex + 1// 5. 退出递归条件if (index < newDiaries.length){if (isShowLoadProcess){loadTimeOutHandle.value = setTimeout(()=>{renderingHoleList(newDiaries, index)}, loadGap)} else {renderingHoleList(newDiaries, index)}}})
}

相关文章:

实现思路:Vue 子组件高度不固定下实现瀑布流布局

实现思路&#xff1a;Vue 子组件高度不固定下实现瀑布流布局 一、瀑布流布局基础实现原理 在深入解说不定高度子组件的瀑布流如何实现之前&#xff0c;先大体说一下子组件高度固定已知的这种实现原理&#xff1a; 有一个已知组件高度的数组。定义好这个瀑布流的列数&#xff…...

构建实时搜索与推荐系统:Elasticsearch与业务结合

在当今这个信息爆炸的时代&#xff0c;搜索引擎和推荐系统已成为我们日常生活中不可或缺的一部分。它们不仅帮助我们快速找到所需信息&#xff0c;还根据我们的喜好推荐相关内容&#xff0c;提升了用户体验。本文将探讨如何使用Elasticsearch构建实时搜索与推荐系统&#xff0c…...

鸿蒙 如何将base64的图片保存到相册

把一个base64图片 保存到鸿蒙手机 相册中怎么实现呢&#xff1f; 下面有2中方法 方案一&#xff1a;可以通过安全控件「保存控件&#xff08;SaveButton&#xff09;」实现。该控件对应媒体库写入特权。应用集成保存控件后&#xff0c;用户点击该控件&#xff0c;应用会获取1…...

高速公路智能管理系统:构建安全畅通的数字大动脉

随着城市化进程的加速和交通需求的增长&#xff0c;高速公路系统作为城市交通的重要组成部分&#xff0c;正承担着越来越多的交通运输任务。为了提升高速公路的安全性、便捷性和智能化管理水平&#xff0c;高速公路智能管理系统应运而生。本文将深入探讨高速公路智能管理系统的…...

基于Java自习室在线预约系统 的设计与实现

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Php和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…...

摄像头校准之白平衡畸变坏点

摄像头校准之白平衡&畸变&坏点 1. 源由2. 校准内容3. 畸变校准一、畸变模型二、校准步骤1. 准备工作2. 特征点检测3. 计算内参数和畸变系数4. 畸变校正 三、验证和优化1. 视觉验证2. 误差评估3. 参数优化 4. 白平衡校准一、白平衡基础二、自动白平衡&#xff08;AWB&am…...

【C++进阶】模板进阶与仿函数:C++编程中的泛型与函数式编程思想

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;栈和队列相关知识 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀模板进阶 &#x1f9e9;<&…...

OpenCV之cv::Scalar

在 OpenCV 中&#xff0c;cv::Scalar 是一个模板类&#xff0c;用于表示多通道的值。常用来表示颜色或其他具有多个分量的数据。在图像处理中&#xff0c;cv::Scalar 经常用于指定颜色。 cv::Scalar(255, 255, 255) 具体如何理解&#xff0c;取决于图像的颜色空间&#xff1a;…...

智能合约与身份验证:区块链技术的创新应用

一、引言 区块链&#xff0c;一个近年来备受瞩目的技术名词&#xff0c;已经从最初的数字货币领域扩展到了众多行业。那么&#xff0c;究竟什么是区块链&#xff1f;它为何如此重要&#xff1f;本文将深入剖析区块链技术的原理、应用及未来发展。 二、区块链的基本概念 区块…...

浔川身份证号码查询——浔川python科技社

Python获取身份证信息 公民身份号码是每个公民唯一的、终身不变的身份代码&#xff0c;由公安机关按照公民身份号码国家标准编制。每一个居民只能拥有一个唯一的身份证&#xff0c;它是用于证明持有人身份的一种法定证件。 身份证包含了个人的一些重要信息&#xff0c;比如&am…...

C++的标准容器及其应用

C的标准容器及其应用 数组&#xff08;array&#xff09;数组的特征应用实列 前向列表&#xff08;forward_list&#xff09;前向列表的特征应用实列 列表&#xff08;list&#xff09;列表的特征应用实列 有序映射&#xff08;map&#xff09;有序映射的特征应用实列 队列&…...

linux如何部署前端项目和安装nginx

要在Linux上部署前端项目并安装Nginx&#xff0c;你可以按照以下步骤操作&#xff1a; 安装Nginx: sudo apt update sudo apt install nginx 启动Nginx服务: sudo systemctl start nginx 确保Nginx服务开机自启: sudo systemctl enable nginx 部署前端项目&#xff0c;假设前…...

Coolify:24.2K 星星!使用全新、开源免费且自托管的替代方案,部署应用程序的最佳工具(停止使用 Vercel)

✨点击这里✨&#xff1a;&#x1f680;原文链接&#xff1a;&#xff08;更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号&#xff01;&#xff09; Coolify&#xff1a;24.2K 星星&#xff01;使用全新、开源免费且自托管的替代方案&#xff0c;部…...

Dubbo入门

Dubbo&#xff0c;听名字好像有点高大上&#xff0c;但实际上它就是个让不同的计算机程序之间能够互相交流的工具&#xff0c;专业点说&#xff0c;它是一个分布式服务框架。想象一下&#xff0c;你有好几个小团队&#xff0c;每个团队负责开发一个部分&#xff0c;最后这些部分…...

从零学习es8

配置 编辑 elasticsearch.yml xpack.security.enabled: true 单节点 discovery.type: single-node设置账号&#xff1a; elasticsearch-reset-password -u elastic 如果要将密码设置为特定值&#xff0c;请使用交互式 (-i) 参数运行该命令。 elasticsearch-reset-password -i…...

String.compareTo()方法详解

Java 中的 String.compareTo() 方法用于按字典顺序比较两个字符串。这个方法实现了 Comparable 接口&#xff0c;返回一个整数&#xff0c;表示字符串的相对顺序。 方法签名 public int compareTo(String anotherString)返回值 一个负整数&#xff1a;如果当前字符串在字典顺…...

Nintex流程平台引入生成式人工智能,实现自动化革新

工作流自动化提供商Nintex宣布在其Nintex流程平台上推出一系列新的人工智能驱动改进。这些增强显著减少了文档化、管理和自动化业务流程所需的时间。这些新特性为Nintex流程平台不断扩展的人工智能能力增添了新的亮点。 Nintex首席产品官Niranjan Vijayaragavan表示&#xff1a…...

永远不要做房间里“最聪明的人”(早懂早受益)

听好了&#xff0c;茶客&#xff0c;我要向你解释一些事情。 你的工作和职责是让客户认为他是房间里最聪明的人。 如果你完成了这项任务之后&#xff0c;还有多余的精力&#xff0c;应该用它来让你的高级合伙人显得像是房间里第二聪明的人。 只有履行了这两项义务之后&#xff…...

Leetcode 3177. Find the Maximum Length of a Good Subsequence II

Leetcode 3177. Find the Maximum Length of a Good Subsequence II 1. 解题思路2. 代码实现 题目链接&#xff1a;3177. Find the Maximum Length of a Good Subsequence II 1. 解题思路 这一题我一开始的思路是直接使用暴力的动态规划的方式进行实现&#xff0c;结果遇到了…...

程序员做电子书产品变现的复盘(2)

赚钱有多种&#xff0c;简单分为两类。 &#xff08;1&#xff09;手停口停型&#xff0c;这种工作在你积极从事时可能每天能带来数千甚至上万的收入&#xff0c;但一旦停止工作&#xff0c;收入就会大幅下降甚至归零&#xff0c;例如我们的日常工资。 &#xff08;2&#xf…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用

前言&#xff1a;我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM&#xff08;Java Virtual Machine&#xff09;让"一次编写&#xff0c;到处运行"成为可能。这个软件层面的虚拟化让我着迷&#xff0c;但直到后来接触VMware和Doc…...