【写在前面】

最近在刷掘金的时候看到一篇关于瀑布流布局的文章,然鹅他们的实现都是前端的那套,就想着 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) 即可:

    if (flickable.currentColumn == flickable.column) {
flickable.currentColumn = 0;
flickable.currentX = 0;
for (let i = 0; i < flickable.column; i++) {
flickable.currentY[i] += flickable.prevHeight[i];
}
} x = flickable.currentX;
y = flickable.currentY[flickable.currentColumn]; flickable.prevHeight[flickable.currentColumn] = Math.round(height + flickable.spacing); print(flickable.currentColumn, flickable.currentX, flickable.prevHeight, flickable.currentY); flickable.currentX += coverRealWidth + flickable.spacing; flickable.currentColumn++; let max = 0;
for (let j = 0; j < flickable.column; j++) {
max = Math.max(flickable.prevHeight[j] + flickable.currentY[j]);
} flickable.contentHeight = max;

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

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

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

Repeater {
id: repeater
model: ListModel {
id: listModel
Component.onCompleted: {
flickable.loadMore();
}
}
delegate: Rectangle {
id: rootItem
width: (flickable.width - flickable.spacing) / flickable.column
height: coverRealHeight + titleHeight + infoHeight
radius: 4
clip: true property real aspectRatio: coverWidth / coverHeight
property real coverRealWidth: width
property real coverRealHeight: width / aspectRatio
property real titleWidth: width
property real titleHeight: titleText.height
property real infoWidth: width
property real infoHeight: 50 Component.onCompleted: {
if (flickable.currentColumn == flickable.column) {
flickable.currentColumn = 0;
flickable.currentX = 0;
for (let i = 0; i < flickable.column; i++) {
flickable.currentY[i] += flickable.prevHeight[i];
}
} x = flickable.currentX;
y = flickable.currentY[flickable.currentColumn]; flickable.prevHeight[flickable.currentColumn] = Math.round(height + flickable.spacing); print(flickable.currentColumn, flickable.currentX, flickable.prevHeight, flickable.currentY); flickable.currentX += coverRealWidth + flickable.spacing; flickable.currentColumn++; let max = 0;
for (let j = 0; j < flickable.column; j++) {
max = Math.max(flickable.prevHeight[j] + flickable.currentY[j]);
} flickable.contentHeight = max;
} Column {
Item {
id: coverPort
width: coverRealWidth
height: coverRealHeight Image {
anchors.fill: parent
anchors.topMargin: rootItem.radius
source: cover
}
} Item {
id: titlePort
width: titleWidth
height: titleText.height Text {
id: titleText
width: parent.width
wrapMode: Text.WrapAnywhere
text: title
font.family: "微软雅黑"
font.pointSize: 14
}
} Item {
id: infoPort
width: infoWidth
height: infoHeight RowLayout {
anchors.fill: parent CircularImage {
id: head
Layout.preferredWidth: parent.height - 5
Layout.preferredHeight: parent.height - 5
Layout.leftMargin: 5
Layout.alignment: Qt.AlignVCenter
source: "file:/C:/Users/mps95/Desktop/head.jpg"
} Text {
Layout.fillWidth: true
Layout.fillHeight: true
text: "用户" + user
font.pointSize: 14
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
} Text {
Layout.preferredWidth: 100
Layout.preferredHeight: parent.height
Layout.rightMargin: 5
text: (like ? "" : "") + " " + Math.round(Math.random() * 1000)
font.pointSize: 14
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
property int like: Math.round(Math.random())
}
}
}
}
}
}

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

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

【结语】

最后:项目链接(多多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. 题解:AT_abc360_c [ABC360C] Move It

    背景 机房大佬掉大分了,乐悲. 题意 给你几个箱子和每个箱子里装有的东西 \(a\) 和对应的重量 \(w\),现在要让每个箱子里都装有一个东西,每次可以移动任意一个箱子中的任意一个东西,代价为它的重 ...

  2. EF6/EFCore Code-First Timestamp SQL Server

    EF 6和EF Core都包含TimeStamp数据注解特性.它只能用在实体的byte数组类型的属性上,并且只能用在一个byte数组类型的属性上.然后在数据库中,创建timestamp数据类型的列,在 ...

  3. vue项目中实现sql编辑器功能自定义高亮词汇可提示关键词-codemirror

    先上图:左侧是数据库表,右侧上部是sql编辑器,下部是执行sql的返回接口 HTML: <el-row> <el-col :span="4" class=&quo ...

  4. 对比python学julia(第一章)--(第四节)冰雹猜想

    4.1 依葫芦画瓢 冰雹猜想是一种非常有趣的数字黑洞,曾让无数的数学爱好者为之痴迷.它有一个非常简单的变换规则,具体来说就是:任意取一个正整数n,如果n是偶数,就把n变成n/2;如果n是奇数,就把n变 ...

  5. 【RabbitMQ】11 深入部分P4 延迟队列

    一.延迟队列: 消息经过交换机分配到队列上之后,在到达指定的时间,才会被消费? 需求: 1.下单之后的30分钟,用户未支付,订单取消,回滚库存 2.新用户注册7天后,发送短信慰问,或者是用户生日发送短 ...

  6. 【Windows】XP系统安装TIM/QQ 数字签名过期问题

    需要手动安装数字签名 右键安装包 -> 属性 但是我的TIM没有用,对QQ是有效的 参考自视频: https://www.bilibili.com/video/av413122971/

  7. 再测python3.13a —— python3.13是否移除了GIL的限制(续)

    前文: python3.13是否移除了GIL的限制 x86_64 ubuntu22.04环境下编译版本python3.13.0 alpha 0源码--python3.13.0 alpha 0的源码编译 ...

  8. 【转载】 传统PID算法解决不了的情况,应该怎么办?

    原文地址: http://www.51hei.com/bbs/dpj-152844-1.html --------------------------------------------------- ...

  9. 域名所有权验证 —— DNS TXT 域名验证

    参考: https://help.aliyun.com/zh/cdn/getting-started/verify-the-ownership-of-a-domain-name https://blo ...

  10. ubuntu18.04 源码方式安装wine , 警告,libxrender 64-bit development files not found, XRender won't be supported.

    警告信息: configure: WARNING: libxrender 64-bit development files not found, XRender won't be supported. ...