【写在前面】

最近在刷掘金的时候看到一篇关于瀑布流布局的文章,然鹅他们的实现都是前端的那套,就想着 Qml 有没有类似实现。

结果百度了一圈也没有( T_T Qml 凉了凉了 ),于是,我按照自己理解,简单实现了一个 Qml 版的瀑布流布局。

关于瀑布流:

瀑布流布局(Waterfall Layout),也被称为瀑布式布局或多栏自适应布局,是一种网页布局技术,它允许内容以多列的形式显示,类似于瀑布一样从上到下流动。这种布局方式特别适合于展示图片或卡片式内容,如图片库、新闻摘要、商品列表等。

瀑布流布局的特点包括:

  1. 多列显示:内容被分割成多列,每列可以独立滚动,使得页面可以展示更多的信息。
  2. 动态宽度:每列的宽度通常是固定的,而内容块(如图片或卡片)的宽度可以是动态的,以适应不同的屏幕大小。
  3. 不等高:内容块的高度可以不同,这样可以使布局看起来更加自然和有吸引力。
  4. 响应式:布局可以根据用户的屏幕尺寸自动调整,以提供最佳的浏览体验。
  5. 灵活性:内容块可以自由地在列之间流动,不需要严格的对齐。

【正文开始】

一个经典的瀑布流布局来自小红书:

而我们实现的 Qml 版效果图如下:

现在开始讲解思路:

首先考虑屏幕宽度,竖屏两列,横屏可以三列或者更多,应当根据宽度动态改变,然后便可以计算出列宽:

width: (flickable.width - flickable.spacing) / flickable.column

因此,其实未知的仅有卡片高度:

如图所示,卡片高度由三部分组成:【封面图片高度】+【标题高度】+【卡片信息高度】

height: coverRealHeight + titleHeight + infoHeight

现在有了宽高,接下来只要计算出 位置 (x, y) 即可:

  1. if (flickable.currentColumn == flickable.column) {
  2. flickable.currentColumn = 0;
  3. flickable.currentX = 0;
  4. for (let i = 0; i < flickable.column; i++) {
  5. flickable.currentY[i] += flickable.prevHeight[i];
  6. }
  7. }
  8. x = flickable.currentX;
  9. y = flickable.currentY[flickable.currentColumn];
  10. flickable.prevHeight[flickable.currentColumn] = Math.round(height + flickable.spacing);
  11. print(flickable.currentColumn, flickable.currentX, flickable.prevHeight, flickable.currentY);
  12. flickable.currentX += coverRealWidth + flickable.spacing;
  13. flickable.currentColumn++;
  14. let max = 0;
  15. for (let j = 0; j < flickable.column; j++) {
  16. max = Math.max(flickable.prevHeight[j] + flickable.currentY[j]);
  17. }
  18. flickable.contentHeight = max;

x 坐标计算思路是:从左往右依次增加一个卡片宽度,到达本行最后一个卡片时置零即可。

y 坐标计算思路是:记录下本行卡片高度数组 prevHeight[column],到达本行最后一个卡片时计算下行卡片 y 坐标数组 currentY[column],而首行则为 0。

至此,Rect (x, y, width, height) 全部已知,我们可以直接利用 Repeater 轻松实例化出来:

  1. Repeater {
  2. id: repeater
  3. model: ListModel {
  4. id: listModel
  5. Component.onCompleted: {
  6. flickable.loadMore();
  7. }
  8. }
  9. delegate: Rectangle {
  10. id: rootItem
  11. width: (flickable.width - flickable.spacing) / flickable.column
  12. height: coverRealHeight + titleHeight + infoHeight
  13. radius: 4
  14. clip: true
  15. property real aspectRatio: coverWidth / coverHeight
  16. property real coverRealWidth: width
  17. property real coverRealHeight: width / aspectRatio
  18. property real titleWidth: width
  19. property real titleHeight: titleText.height
  20. property real infoWidth: width
  21. property real infoHeight: 50
  22. Component.onCompleted: {
  23. if (flickable.currentColumn == flickable.column) {
  24. flickable.currentColumn = 0;
  25. flickable.currentX = 0;
  26. for (let i = 0; i < flickable.column; i++) {
  27. flickable.currentY[i] += flickable.prevHeight[i];
  28. }
  29. }
  30. x = flickable.currentX;
  31. y = flickable.currentY[flickable.currentColumn];
  32. flickable.prevHeight[flickable.currentColumn] = Math.round(height + flickable.spacing);
  33. print(flickable.currentColumn, flickable.currentX, flickable.prevHeight, flickable.currentY);
  34. flickable.currentX += coverRealWidth + flickable.spacing;
  35. flickable.currentColumn++;
  36. let max = 0;
  37. for (let j = 0; j < flickable.column; j++) {
  38. max = Math.max(flickable.prevHeight[j] + flickable.currentY[j]);
  39. }
  40. flickable.contentHeight = max;
  41. }
  42. Column {
  43. Item {
  44. id: coverPort
  45. width: coverRealWidth
  46. height: coverRealHeight
  47. Image {
  48. anchors.fill: parent
  49. anchors.topMargin: rootItem.radius
  50. source: cover
  51. }
  52. }
  53. Item {
  54. id: titlePort
  55. width: titleWidth
  56. height: titleText.height
  57. Text {
  58. id: titleText
  59. width: parent.width
  60. wrapMode: Text.WrapAnywhere
  61. text: title
  62. font.family: "微软雅黑"
  63. font.pointSize: 14
  64. }
  65. }
  66. Item {
  67. id: infoPort
  68. width: infoWidth
  69. height: infoHeight
  70. RowLayout {
  71. anchors.fill: parent
  72. CircularImage {
  73. id: head
  74. Layout.preferredWidth: parent.height - 5
  75. Layout.preferredHeight: parent.height - 5
  76. Layout.leftMargin: 5
  77. Layout.alignment: Qt.AlignVCenter
  78. source: "file:/C:/Users/mps95/Desktop/head.jpg"
  79. }
  80. Text {
  81. Layout.fillWidth: true
  82. Layout.fillHeight: true
  83. text: "用户" + user
  84. font.pointSize: 14
  85. verticalAlignment: Text.AlignVCenter
  86. elide: Text.ElideRight
  87. }
  88. Text {
  89. Layout.preferredWidth: 100
  90. Layout.preferredHeight: parent.height
  91. Layout.rightMargin: 5
  92. text: (like ? "" : "") + " " + Math.round(Math.random() * 1000)
  93. font.pointSize: 14
  94. horizontalAlignment: Text.AlignRight
  95. verticalAlignment: Text.AlignVCenter
  96. property int like: Math.round(Math.random())
  97. }
  98. }
  99. }
  100. }
  101. }
  102. }

loadMore() 是向后台请求更多的卡片数据,这部分需要根据实际需求进行改造,我这里就简单生成了一些模拟数据:

  1. function loadMore() {
  2. //这部分来自后台请求, 必须知道封面宽高
  3. let titleList = [
  4. "单行标题: 测试测试测试测试",
  5. "双行标题: 测试测试测试测试测试测测试测试测试测试测试测试",
  6. "三行标题: 测试测试测试测试测试测测试测试测试测试测试测试测试测试测试测试测试测试测试"
  7. ];
  8. for (let i = 0; i < 10; i++) {
  9. let userId = Math.round(Math.random() * 100000);
  10. let type = Math.round(Math.random()); //0 image / 1 video
  11. let cover = "file:/C:/Users/mps95/Desktop/素材/动漫图片/img2" + i + ".jpg"; //封面, 无论视频还是图片都需要有
  12. let url = cover;
  13. if (type == 1) {
  14. //url = "file:/test.mp4";
  15. }
  16. let object = {
  17. type: type,
  18. cover: cover,
  19. user: userId,
  20. url: url,
  21. title: titleList[Math.round(Math.random() * 2)],
  22. coverWidth: 300,
  23. coverHeight: (type + 2) * 100 + Math.round(Math.random() * 3) * 80
  24. };
  25. jsonData.push(object);
  26. listModel.append(object);
  27. }
  28. }

【结语】

最后:项目链接(多多star呀.._):

Github 的 WaterfallFlow 瀑布流视图(并且可以自适应),类似小红书

注意: 测试用的图片没有包含在内,请改为自己的测试集。

Qml 实现瀑布流布局的更多相关文章

  1. JS瀑布流布局

    好久没有更新博客喽,今天来说一个瀑布流布局. 瀑布流在很多网站已有很多,现在只说一下简单的实现原理吧, 1.计算一行可以排放几个元素 2.建立一个数组用于存放第一行的每个元素的高度. 3.得到数组中的 ...

  2. CSS3学习总结——实现瀑布流布局与无限加载图片相册

    首先给大家看一下瀑布流布局与无限加载图片相册效果图: 一.pic1.html页面代码如下: <!DOCTYPE html> <html> <head> <me ...

  3. myWaterfall - jQuery瀑布流布局插件

    myWaterfall - jQuery瀑布流布局插件 Demo http://jsfiddle.net/q3011893/p5k2ogy8/embedded/result,html,css,js/ ...

  4. jquery实现简单瀑布流布局(续):图片懒加载

    # jquery实现简单瀑布流布局(续):图片懒加载 这篇文章是jquery实现简单瀑布流布局思想的小小扩展.代码基于前作的代码继续完善. 图片懒加载就是符合某些条件时才触发图片的加载.最常见的具体表 ...

  5. jquery实现简单瀑布流布局

    jquery实现简单瀑布流布局 是开头都会说的原理 瀑布流布局有两种,一种是固定列,一种是非固定列.在此主要记述第一种的实现. 固定列的特征是:无论页面如何缩放,每行的总列数都一致. 一行4列的瀑布流 ...

  6. JavaScript——基本的瀑布流布局及ajax动态新增数据

    本文用纯js代码手写一个瀑布流网页效果,初步实现一个基本的瀑布流布局,以及滚动到底部后模拟ajax数据加载新图片功能. 缺点: 1. 程序不是响应式,不能实时调整页面宽度: 2. 程序中当新增ajax ...

  7. flexbox实现不等宽不等高的瀑布流布局

    第一次做不等宽不等高的瀑布流布局,刚开始企图用ccs3的column属性+flexbox来实现,瞎捣鼓半天都没有能弄好, 弱鸡哭晕在厕所(┬_┬),气的午饭都没有吃. 后来逼着自己冷静下来,又捣鼓了1 ...

  8. 通过Measure & Arrange实现UWP瀑布流布局

    简介 在以XAML为主的控件布局体系中,有用于完成布局的核心步骤,分别是measure和arrange.继承体系中由UIElement类提供Measure和Arrange方法,并由其子类Framewo ...

  9. jQuery Wookmark Load 瀑布流布局实例演示

    瀑布流布局非常适合大量图片的展示,一改过去裁剪图片尺寸统一的排版,每张图片都能完全展示,并错落有致,让人眼前一亮. 版本: jQuery v1.4.3+ jQuery Wookmark Load v1 ...

  10. 也来谈谈wap端瀑布流布局

    Definition 瀑布流布局,在视觉上表现为参差不齐的多栏布局,随着页面滚动条向下滚动,新数据不断被加载进来. 瀑布流对于图片的展现,是高效而具有吸引力的,用户一眼扫过的快速阅读模式可以在短时间内 ...

随机推荐

  1. yb课堂之个人信息接口开发 《十三》

    根据token从查询个人信息接口开发 直接解密token,获取个人信息 通过token解密查询数据库获取个人信息 UserController.java package net.ybclass.onl ...

  2. [oeasy]python0023_[趣味拓展]Guido的简历_从ABC到python

    Guido的简历 回忆上次内容 上次 添加了 各种 符号 铭文 各种 颜色 铸造了 自己的宝剑       添加图片注释,不超过 140 字(可选)   这些都是 用python画出来的宝剑   py ...

  3. [rCore学习笔记 03]配置rCore开发环境

    写在前面 本随笔是非常菜的菜鸡写的.如有问题请及时提出. 可以联系:1160712160@qq.com GitHhub:https://github.com/WindDevil (目前啥也没有 rCo ...

  4. sed 进阶使用

    sed 进阶使用 工作原理 sed 维护两个数据缓冲区: 活动模式空间 和 辅助保持空间 两者最初都是空的 sed 通过对每一行输入执行以下循环进行操作 从输入流中读取一行,删除任何尾随的换行符,并将 ...

  5. 靶机: Chronos

    靶机: Chronos 准备 靶机:https://www.vulnhub.com/entry/chronos-1,735/ 使用 VirtualBox 网络 Host-Only 配置网络环境:htt ...

  6. 《最新出炉》系列入门篇-Python+Playwright自动化测试-54- 上传文件(input控件) - 上篇

    1.简介 在实际工作中,我们进行web自动化的时候,文件上传是很常见的操作,例如上传用户头像,上传身份证信息等.所以宏哥打算按上传文件的分类对其进行一下讲解和分享. 2.上传文件的API(input控 ...

  7. vue3 + ts 中出现 类型“typeof import(".........../node_modules/vue/dist/vue")”的参数不能赋给类型“Component<any, any, any, ComputedOptions, MethodOptions>”的参数。

    错误示例截图 解决方法 修改shims-vue.d.ts中的内容 declare module "*.vue" { import { defineComponent } from ...

  8. 【Mybatis】05 官方文档指北阅读 vol3 配置 其二

    对象工厂(objectFactory)[省略,入门阶段实在不懂] 插件(plugins)[省略,入门阶段实在不懂] 环境配置(environments) MyBatis 可以配置成适应多种环境,这种机 ...

  9. FFmpeg在游戏视频录制中的应用:画质与文件大小的综合比较

    我们游戏内的视频录制目前只支持avi固定码率,在玩家见面会上有玩家反馈希望改善录制画质,我最近在研究了有关视频画质的一些内容并做了一些统计. 录制视频大小对比 首先在游戏引擎中增加了对录制mp4格式的 ...

  10. 人形机器人专用操作系统 —— KaihongOS还是ROS

    机器人不是一个新词汇,机器人在人们生产生活中已经出现了几十年了,而最近最火的词汇是"智能机器人"或者是"人形机器人(humanoid)",而这二者之间的区别就是 ...