[Writing in front]
Recently in the brush nuggets when I saw an article on the layout of the waterfall, but the goose is their implementation of the front-end of the set, I thought Qml there is no similar implementation.
The result of a circle of Baidu also did not ( T_T Qml cool cool cool ), so I in accordance with their own understanding, a simple implementation of a Qml version of the waterfall layout.
About the waterfall stream:
Waterfall Layout, also known as Waterfall Layout or Multi-column Adaptive Layout, is a web layout technique that allows content to be displayed in multiple columns, similar to a waterfall flowing from top to bottom. This layout is particularly suitable for displaying images or card-based content such as photo galleries, news summaries, product listings, etc.
Features of the waterfall layout include:
- multicolumn display: The content is split into multiple columns, each of which can be scrolled independently, allowing the page to display more information.
- Dynamic width: The width of each column is usually fixed, while the width of content blocks (such as images or cards) can be dynamic to accommodate different screen sizes.
- unequal: Content blocks can have different heights, which makes the layout look more natural and attractive.
- responsive: The layout automatically adjusts to the user's screen size to provide an optimal viewing experience.
- dexterity: Content blocks can flow freely between columns without strict alignment.
[Text Begins]
A classic waterfall layout comes from Little Red Book:
And the Qml version of our implementation is shown below:
Now on to the ideas:
First consider the width of the screen, vertical screen two columns, horizontal screen can be three or more columns, should be dynamically changed according to the width, then you can calculate the column width:
width: ( - ) /
Therefore, the only thing that is actually unknown is the height of the card:
The height of the card consists of three parts as shown:[Height of cover image] + [Height of title] + [Height of card information]
。
height: coverRealHeight + titleHeight + infoHeight
Now that you have the width and height, the next step is to just calculate thePosition (x, y)
Ready to go:
if ( == ) {
= 0;
= 0;
for (let i = 0; i < ; i++) {
[i] += [i];
}
}
x = ;
y = [];
[] = (height + );
print(, , , );
+= coverRealWidth + ;
++;
let max = 0;
for (let j = 0; j < ; j++) {
max = ([j] + [j]);
}
= max;
The idea of calculating the x-coordinate is to increase the width of the cards one by one from left to right, and then set it to zero when you reach the last card in the row.
The idea of calculating the y-coordinate is to record an array of the heights of the cards in the lineprevHeight[column]
When the last card in the row is reached, an array of y-coordinates of the next card is computed.currentY[column]
and the first line is 0.
Up to this point.Rect (x, y, width, height)
All are known, and we can directly utilize theRepeater
Easily instantiated:
Repeater {
id: repeater
model: ListModel {
id: listModel
: {
();
}
}
delegate: Rectangle {
id: rootItem
width: ( - ) /
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:
property real infoWidth: width
property real infoHeight: 50
: {
if ( == ) {
= 0;
= 0;
for (let i = 0; i < ; i++) {
[i] += [i];
}
}
x = ;
y = [];
[] = (height + );
print(, , , );
+= coverRealWidth + ;
++;
let max = 0;
for (let j = 0; j < ; j++) {
max = ([j] + [j]);
}
= max;
}
Column {
Item {
id: coverPort
width: coverRealWidth
height: coverRealHeight
Image {
: parent
:
source: cover
}
}
Item {
id: titlePort
width: titleWidth
height:
Text {
id: titleText
width:
wrapMode:
text: title
: "Microsoft Black and White (computer science)"
: 14
}
}
Item {
id: infoPort
width: infoWidth
height: infoHeight
RowLayout {
: parent
CircularImage {
id: head
: - 5
: - 5
: 5
:
source: "file:/C:/Users/mps95/Desktop/"
}
Text {
: true
: true
text: "subscribers" + user
: 14
verticalAlignment:
elide:
}
Text {
: 100
:
: 5
text: (like ? "🩷" : "🤍") + " " + (() * 1000)
: 14
horizontalAlignment:
verticalAlignment:
property int like: (())
}
}
}
}
}
}
loadMore()
is to request more card data from the backend, this part needs to be transformed according to the actual needs, I simply generate some simulated data here:
function loadMore() {
//This part is requested from the backend and must know the cover width and height.
let titleList = [
"One line title: Test Test Test Test Test Test",
"Two line title: test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test",
];
for (let i = 0; i < 10; i++) {
let userId = (() * 100000); let type = (()); {
let type = (()); //0 image / 1 video
let cover = "file:/C:/Users/mps95/Desktop/Materials/Anime Pictures/img2" + i + ".jpg"; //cover, both video and image are required
let url = cover.
if (type == 1) {
//url = "file:/test.mp4"; }
}
let object = {
type: type, cover: cover, } let object = {
cover: cover, user: userId,
user: userId,
cover: cover, user: userId, url: url, title: titleList[(( * 2)], titleList[((() * 2)], titleList[() * 2)
title: titleList[(() * 2)],
coverWidth: 300, coverHeight: (type + 2) * 100 + (() * 2)
coverHeight: (type + 2) * 100 + (() * 3) * 80
}.
(object).
(object); (object).
}
}
[Conclusion]
And finally: project link (more star yah...). ⭐_⭐):
Github'sWaterfallFlow waterfall view (and can be adaptive), similar to Little Red Book
Attention: Images for testing are not included, please change to your own test set.