Make writing a habit together! This is the sixth day of my participation in the “Gold Digging Day New Plan ยท April More text Challenge”. Click here for more details.
Said in the previous
๐ has been working on his personal blog website recently. There is a module that needs the waterfall flow picture ๐ผ to show, so I packaged it into a component, which can be imported and used later. The specific effects are as follows: ๐ :
What is waterfall flow
Waterfall flow, also known as waterfall flow layout. A popular web page layout that visually presents a jagged, multi-column layout that loads blocks of data and appends them to the current end as the page scrollbars scroll down. Pinterest was one of the first sites to use this layout, and it became popular in China. Most of the fresh stations in China are basically in this style.
Implementation approach
There are two ways to realize waterfall flow layout: one is to fix the width of the picture and the width of the container, and dynamically calculate the column number of the display picture; The other is to fix the number of columns displayed in the image and dynamically calculate the width of the image by the width of the container. Let’s take a look at the two different implementations.
First, fixed picture width
Suppose we now have a container and some images, how do we use the waterfall layout to display the images in the container?
1. Calculate the number of image columns that the container can display
ImgWidth = imgWidth; contentWidth = contentWidth; ParseInt (contentWidth/imgWidth) or math.floor, where the maximum number of columns should be the container width divided by the image width, as shown below:
There’s not enough space left for one more image, so round down.
Fill each column with images
Once we know the number of columns, we can start filling each column with an image, as shown below. The container can be divided into five columns.
Next fill the container with the image below
As shown in the animation above, the final result is as follows (numbers represent the order of images) :
Every time an image is inserted, the column with the lowest current height is always selected. If the sixth image should be inserted below the second column of the second column, how can this be done with the code?
3. Code implementation
html
<template>
<div id="img-content" class="img-content">
<img
class="img"
:style="'width:' + imgWidth + 'px; '"
v-for="(item, index) in imgList"
:key="'img' + index"
:src="item"
/>
</div>
</template>
Copy the code
CSS
<style lang="scss" scoped>
.img-content {
height: 30vh;
width: 320px;
position: relative;
.img {
position: absolute;
vertical-align: top;
margin: 3px;
}
}
</style>
Copy the code
JavaScript
Parameter configuration
The parameters we need to receive are:
- (1) imgList: list of pictures displayed;
- (2) imgWidth: the size of the image;
- (3) imgMargin: the space between images.
props: {
imgList: {
type: Array.default: () = >[]},imgWidth: {
type: Number.default: 100
},
imgMargin: {
type: Number.default: 3}},Copy the code
Image location calculation
- (1) heightArr: Height array
We can define a heightArr variable to store the current usage height of each column.
- (2) minIndex: index of the column with the lowest current height
When inserting, you need to find the column with the lowest current height and insert the image into that column.
- (3) Assign top and left to the picture
// Height The smallest height of the array
const minHeight = Math.min(... heightArr);// Index of the smallest height of the height array
const minIndex = heightArr.indexOf(minHeight);
item.style.top = minHeight + "px";
item.style.left = minIndex * imgWidth + "px";
Copy the code
- (4) Complete code
methods: {
waterfallHandler() {
const imgWidth = this.imgWidth + this.imgMargin * 2;
const contentW = document.getElementById("img-content").offsetWidth;
// Get the column number of the image
const column = parseInt(contentW / imgWidth);
// Height array
const heightArr = new Array(column).fill(0);
const imgList = document.getElementsByClassName("img");
for (let i = 0; i < imgList.length; i++) {
const item = imgList[i];
// Height of the current element
const itemHeight = item.offsetHeight;
// Height The smallest height of the array
const minHeight = Math.min(... heightArr);// Index of the smallest height of the height array
const minIndex = heightArr.indexOf(minHeight);
item.style.top = minHeight + "px";
item.style.left = minIndex * imgWidth + "px"; heightArr[minIndex] += itemHeight; }}}Copy the code
- (5) Monitor page size changes
Recalculate when the page size changes.
window.onresize = () = > {
return (() = > {
this.waterfallHandler(); }) (); };Copy the code
4. Complete code
<template>
<div id="img-content" class="img-content">
<img
class="img"
:style="'width:' + imgWidth + 'px; '"
v-for="(item, index) in imgList"
:key="'img' + index"
:src="item"
/>
</div>
</template>
<script>
export default {
name: "JWaterfall".props: {
imgList: {
type: Array.default: () = >[]},imgWidth: {
type: Number.default: 100
},
imgMargin: {
type: Number.default: 3}},mounted() {
window.onresize = () = > {
return (() = > {
this.waterfallHandler(); }) (); };this.waterfallHandler();
},
methods: {
waterfallHandler() {
const imgWidth = this.imgWidth + this.imgMargin * 2;
const contentW = document.getElementById("img-content").offsetWidth;
// Get the column number of the image
const column = parseInt(contentW / imgWidth);
// Height array
const heightArr = new Array(column).fill(0);
const imgList = document.getElementsByClassName("img");
for (let i = 0; i < imgList.length; i++) {
const item = imgList[i];
// Height of the current element
const itemHeight = item.offsetHeight;
// Height The smallest height of the array
const minHeight = Math.min(... heightArr);// Index of the smallest height of the height array
const minIndex = heightArr.indexOf(minHeight);
item.style.top = minHeight + "px";
item.style.left = minIndex * imgWidth + "px"; heightArr[minIndex] += itemHeight; }}}};</script>
<style lang="scss" scoped>
.img-content {
height: 30vh;
width: 320px;
position: relative;
.img {
position: absolute;
vertical-align: top;
margin: 3px; }}</style>
Copy the code
Two, the number of fixed picture columns
1. Calculate the width of each column
Let the number of columns displayed becolumn
, the container width iscontentWidth
, then the width of each column of the container should be:parseInt(contentWidth / column)
Or use math.floor, where the width of each image should beparseInt(contentWidth / column)
, as shown below:
Fill each column with images
As shown above, each LI represents a column that you floatfloat:left;
Therefore, this method does not need to calculate the specific coordinate value of the picture, only need to insert the picture into the lowest height column, the specific implementation steps are similar to the previous method, but also need to constantly insert from the lowest height column, I will not repeat again.
3. Code implementation
html
<template>
<div id="j-water-fall-content"></div>
</template>
Copy the code
CSS
<style lang="scss" scoped>
#j-water-fall-content {
margin: 0;
padding: 0;
}
</style>
Copy the code
JavaScript
Parameter configuration
The parameters we need to receive are:
- (1) imgList: list of pictures displayed;
- (2) column: the number of displayed columns;
- (3) imgMargin: the space between images.
props: {
imgList: {
type: Array.default: () = >[]},column: {
type: Number.default: 4
},
imgMargin: {
type: Number.default: 0.5}},Copy the code
Dynamically inserting images
createImg() {
const ul = document.getElementById("j-water-fall-content");
let trueWidth = Math.floor(
(100 - this.column * this.imgMargin * 2) / this.column
);
for (let i = 0; i < this.column; i++) {
let li = document.createElement("li");
li.style.listStyle = "none";
li.style.float = "left";
li.style.width = `${trueWidth}% `;
li.style.margin = ` 0The ${this.imgMargin}% `;
ul.appendChild(li);
this.arr.push(li);
this.minHeight.push(0);
}
let img = new Image();
img.num = 0;
img.src = this.imgList[img.num];
img.style.width = "100%";
// After the image is loaded
img.onload = this.loadHandler;
},
loadHandler(that) {
const img = that.path[0];
const minHeight = this.minHeight;
const arr = this.arr;
// The minimum of the height array
const min = Math.min.apply(null, minHeight);
// The minimum index of the height array
const minIndex = minHeight.indexOf(min);
// Clone the image
const im = img.cloneNode(true);
// Put the image in the container corresponding to the minimum index
arr[minIndex].appendChild(im);
// Update the height of the container for the minimum index
minHeight[minIndex] += im.height;
img.num++;
img.src = this.imgList[img.num];
}
Copy the code
4. Complete code
<template>
<div id="j-water-fall-content"></div>
</template>
<script>
export default {
name: "JWaterfall".props: {
imgList: {
type: Array.default: () = >[]},column: {
type: Number.default: 4
},
imgMargin: {
type: Number.default: 0.5}},data() {
return {
minHeight: [].arr: []}; },mounted() {
this.createImg();
},
methods: {
createImg() {
const ul = document.getElementById("j-water-fall-content");
let trueWidth = Math.floor(
(100 - this.column * this.imgMargin * 2) / this.column
);
for (let i = 0; i < this.column; i++) {
let li = document.createElement("li");
li.style.listStyle = "none";
li.style.float = "left";
li.style.width = `${trueWidth}% `;
li.style.margin = ` 0The ${this.imgMargin}% `;
ul.appendChild(li);
this.arr.push(li);
this.minHeight.push(0);
}
let img = new Image();
img.num = 0;
img.src = this.imgList[img.num];
img.style.width = "100%";
// After the image is loaded
img.onload = this.loadHandler;
},
loadHandler(that) {
const img = that.path[0];
const minHeight = this.minHeight;
const arr = this.arr;
// The minimum of the height array
const min = Math.min.apply(null, minHeight);
// The minimum index of the height array
const minIndex = minHeight.indexOf(min);
// Clone the image
const im = img.cloneNode(true);
// Put the image in the container corresponding to the minimum index
arr[minIndex].appendChild(im);
// Update the height of the container for the minimum index
minHeight[minIndex] += im.height;
img.num++;
img.src = this.imgList[img.num]; }}};</script>
<style lang="scss" scoped>
#j-water-fall-content {
margin: 0;
padding: 0;
}
</style>
Copy the code
Three, contrast
In my opinion, the implementation of method 2 is better than method 1, because method 1 requires an appropriate container width and image width, otherwise there will be a certain space of white space, such a display effect is not beautiful, as shown below:
Because the width of the container is too large to the width of the picture, the right side will be left blank, which will give a poor visual effect. Method 2 can be used to well fill the space of the container, as shown in the following figure:
Package as a component
To make it easier for me to reuse it later, I put it into one of my component libraries so that I can reference it directly when I need it.
use
As shown below, use it in your blogBlog Address:Jyeontu. Xyz/JYeontuBlog…
Component library address
- The document
Jvuewheel: jyeontu. Xyz/jvuewheel / #…
- The source code
Gitee: gitee.com/zheng_yongt…
Past wonderful
In order to learn mo from Yu, I actually developed such a plug-in
Programmer romance – lovers daily small program
JavaScript bidirectional linked list implementation LRU cache algorithm
JavaScript bidirectional linked list implementation LFU cache algorithm
JavaScript implements prefix trees
Vue simple implementation of the word cloud component
Vue + ECharts realizes drilling linkage of map provinces in China
Pure CSS levitates hints and encapsulates them into VUE components
Said in the back
๐ this is JYeontu, now a front-end engineer, who can brush algorithm problems in spare time. Usually, he likes playing badminton ๐ธ and writing some things. He records ๐ for himself, but also hopes that he can help us a little. Thank you for your support, and we’ll see you at ๐.