【写在前面】

最近在刷掘金的时候看到一篇关于瀑布流布局的文章,然鹅他们的实现都是前端的那套,就想着 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. window10设置开机自启动exe的三种方式(亲测有效)

    拷贝文件到自启动位置 路径地址:C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp 通过组策略设置脚本随服务器启动 开始-> ...

  2. 可视化—gojs 超多超实用经验分享(三)

    目录 32.go.Palette 一排放两个 33.go.Palette 基本用法 34.创建自己指向自己的连线 35.设置不同的 groupTemplate 和 linkTemplate 36.监听 ...

  3. python+selenium基础之XPATH定位(第一篇)

    世界上最远的距离大概就是明明看到一个页面元素站在那里,但是我却定位不到!! selenium定位元素的方法有很多种,像是通过id.name.class_name.tag_name.link_text等 ...

  4. Rust 中 *、&、mut、&mut、ref、ref mut 的用法和区别

    Rust 中 *.&.mut.&mut.ref.ref mut 的用法和区别 在 Rust 中,*.ref.mut.& 和 ref mut 是用于处理引用.解引用和可变性的关键 ...

  5. 机器学习:详解是否要使用端到端的深度学习?(Whether to use end-to-end learning?)

    详解是否要使用端到端的深度学习? 假设正在搭建一个机器学习系统,要决定是否使用端对端方法,来看看端到端深度学习的一些优缺点,这样就可以根据一些准则,判断的应用程序是否有希望使用端到端方法. 这里是应用 ...

  6. python统计班级学生

    python统计班级学生 如下场景: 假设我有一个学生类和一个班级类,想要实现的功能为:    执行班级人数增加的操作.获得班级的总人数:    学生类继承自班级类,每实例化一个学生,班级人数都能增加 ...

  7. 对比python学julia(第二章)--(第三节)玫瑰曲线—数学之美

    3.1.问题描述 在数学世界中有一些美丽的曲线图形,有螺旋线.摆线.双纽线.蔓叶线且.心脏线.渐开线.玫瑰曲线.蝴蝶曲线-- 这些形状各异.简有繁别的数学曲线图形为看似枯燥的数学公式披上精彩纷呈的美丽 ...

  8. 9、IDEA集成Github

    9.1.登录Github账号 9.1.1.打开IDEA的Settings界面 如上图所示,打开IDEA的 Settings(设置)界面. 9.1.2.使用账号密码登录(方式一) 如上图所示,在&quo ...

  9. 14、SpringMVC之注解配置

    14.1.概述 在实际工作中,一般使用配置类和注解代替web.xml和SpringMVC配置文件的功能: 在 Servlet3.0 环境中,容器会在类路径中查找实现了 javax.servlet.Se ...

  10. 【SqlServer】01 概念及笔记

    视频地址: https://www.bilibili.com/video/BV1qW411y7Bq 一.什么是数据库? 狭义定义: 数据仓库 广义定义: 对数据进行存储和操作的软件,和数据本身合并称为 ...