【写在前面】

最近在刷掘金的时候看到一篇关于瀑布流布局的文章,然鹅他们的实现都是前端的那套,就想着 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. git http(s) 保存用户密码

    git 常用配置 git记住密码 1.设置记住密码(默认15分钟): git config --global credential.helper cache 2.如果想自己设置时间,可以这样做: gi ...

  2. Centos7 安装 rabbitmq-server-3.7.7 图文教程

    下载 rabbitmq-server wget https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.7/rabbitmq-server-3. ...

  3. webpack4.15.1 学习笔记(四) — Tree shaking

    目录 Tree shaking 原理 标记效果 副作用代码不可被删除 如何实现 Tree shaking 的几种方法 总结 Tree shaking 本质上为了消除无用的js代码,减少加载文件体积的方 ...

  4. JavaScript处理后端返回PDF文件流,在线预览下载PDF文件

    在实际开发业务中,遇到这一需求,即后端返回的pdf文件,是以base64文件流的方式,在此不便操作接口响应等操作,便以上传一个文件转化为文件流的形式模拟 实际应用时,base64Img = res.d ...

  5. Vue查询传参

    通过修改 getWK005 函数来实现这一点.这里的 query 参数就是发送 GET 请求时的查询参数.你可以将需要的条件作为 query 对象的属性传递进去.比如,如果你想要按照特定的条件查询信息 ...

  6. oeasy教您玩转vim - 71 - # 视图view

    ​ 视图view 回忆上次折叠的细节 折叠方式很多 我们一般就用默认的就行 indent 很好用 前提是缩进语法严格到位 这样语法和排版都能同时确保 打开关闭 zc.zo 是打开关闭当前行 zm.zr ...

  7. SpringTask

    SpringTask是spring提供的一个任务调度工具,按照约定的时间自动执行代码逻辑 定时任务框架,即定时自动执行某段代码 应用场景:信用卡每月还款提醒,火车售票系统处理未支付订单 cron表达式 ...

  8. 压力测试工具httperf使用方法

    目录 压力测试工具httperf使用方法 通过tar zxvf解压httperf-0.9.0.tar.gz 进入目录 安装c++编译环境 开始编译 进入编译后的bin目录 开始测试 压力测试工具htt ...

  9. 【Axure RP】Axure RP 9 下载安装及汉化

    本体及破解机下载: http://www.sd173.com/soft/7951.html 汉化补丁教程见: https://blog.csdn.net/weixin_74457789/article ...

  10. 【Centos】RPM安装Mysql8

    先去官网下载RPM包,没想到RPM包是红帽发行版 https://dev.mysql.com/downloads/mysql/ 使用wget直接下载到Centos里面: wget https://cd ...