您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“怎么使用Vue實現一個圖片水平瀑布流插件”,內容詳細,步驟清晰,細節處理妥當,希望這篇“怎么使用Vue實現一個圖片水平瀑布流插件”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
項目主體用的是之前在學習的CRMEB的后端框架來開發,UI使用iView-UI,其余的場景與其他的vue項目一致。
如果不是vue環境,我們的邏輯為
1.獲取所有的p元素
2.獲取盒子的寬度,寬度都是相同,高度不同
3.在浮動布局中每一行的盒子個數不固定,是根據屏幕寬度和盒子寬度決定
4.獲取屏幕寬度
5.求出列數,屏幕寬度 / 盒子寬度 取整
6.瀑布流最關鍵的是第二行的盒子的排布方式,通過獲取第一行盒子中最矮的一個的下標,絕對定位,top是最矮盒子的高度,left是最矮盒子的下標 * 盒子的寬度
7.循環遍歷所有的盒子,通過列數找到第一行所有的盒子,將第一行盒子的高度放入數組,再取出數組中最小的一個的下標,就是第6步思路的第一行盒子中最矮盒子的下標。
8.循環繼續,第二行第一個盒子,通過絕對定位,放進頁面。
9.關鍵,需要將數組中最小的值加上放進的盒子的高度
10.繼續循環,遍歷所有
11.如果想要加載更多,需要判斷最后一個盒子的高度和頁面滾動的距離,再將數據通過創建元素,追加進頁面,再通過瀑布流布局展示
但如果是Vue項目,我們可以把邏輯歸結為以下幾步
1.獲取屏幕寬度
2..獲取盒子的寬度,寬度都是相同,高度不同
3.在浮動布局中每一行的盒子個數不固定,是根據屏幕寬度和盒子寬度決定
4.求出列數,屏幕寬度 / 盒子寬度 取整
5.瀑布流最關鍵的是第二行的盒子的排布方式,通過獲取第一行盒子中最矮的一個的下標,絕對定位,top是最矮盒子的高度,left是最矮盒子的下標 * 盒子的寬度
6.繼續循環,遍歷所有
7.如果想要加載更多,需要判斷最后一個盒子的高度和頁面滾動的距離,再將數據通過創建元素,追加進頁面,再通過瀑布流布局展示
先看下我的html部分
<template>
<div class="tab-container" id="tabContainer">
<div class="tab-item" v-for="(item, index) in pbList" :key="index">
<img :src="item.url" />
</div>
</div>
</template>
<style scoped>
* {
margin: 0;
padding: 0;
}
/* 最外層大盒子 */
.tab-container {
padding-top: 20px;
position: relative;
}
/* 每個小盒子 */
.tab-container .tab-item {
position: absolute;
height: auto;
border: 1px solid #ccc;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
background: white;
/* 元素不能中斷顯示 */
break-inside: avoid;
text-align: center;
}
.tab-container .tab-item img {
width: 100%;
height: auto;
display: block;
}
</style>
核心js部分
<script>
export default {
name:'compList',
props:{
pbList:{
type:Array,
default:()=>{return []}
}
},
data() {
return {
};
},
mounted() {
this.$nextTick(()=>{
this.waterFall("#tabContainer", ".tab-item"); //實現瀑布流
})
},
methods: {
waterFall(
wrapIdName,
contentIdName,
columns = 5,
columnGap = 20,
rowGap = 20
) {
// 獲得內容可用寬度(去除滾動條寬度)
const wrapContentWidth =
document.querySelector(wrapIdName).offsetWidth;
// 間隔空白區域
const whiteArea = (columns - 1) * columnGap;
// 得到每列寬度(也即每項內容寬度)
const contentWidth = parseInt((wrapContentWidth - whiteArea) / columns);
// 得到內容項集合
const contentList = document.querySelectorAll(contentIdName);
// 成行內容項高度集合
const lineConentHeightList = [];
for (let i = 0; i < contentList.length; i++) {
// 動態設置內容項寬度
contentList[i].style.width = contentWidth + "px";
// 獲取內容項高度
const height = contentList[i].clientHeight;
if (i < columns) {
// 第一行按序布局
contentList[i].style.top = "0px";
contentList[i].style.left = contentWidth * i + columnGap * i + "px";
// 將行高push到數組
lineConentHeightList.push(height);
} else {
// 其他行
// 獲取數組最小的高度 和 對應索引
let minHeight = Math.min(...lineConentHeightList);
let index = lineConentHeightList.findIndex(
(listH) => listH === minHeight
);
contentList[i].style.top = minHeight + rowGap +"px";
contentList[i].style.left = (contentWidth + columnGap) * index + "px";
// 修改最小列的高度 最小列的高度 = 當前自己的高度 + 拼接過來的高度 + 行間距
lineConentHeightList[index] += height + rowGap;
}
}
},
},
};
</script>
這里要給大家提個醒,在當插件使用的時候,我們需要用this.$nextTick()來進行頁面初始化,因為方法成功的前提是要等頁面初始化加載完畢后才能進行獲取和計算
<template>
<div class="tab-container" id="tabContainer">
<div class="tab-item" v-for="(item, index) in pbList" :key="index">
<img :src="item.url" />
</div>
</div>
</template>
<script>
export default {
name:'compList',
props:{
pbList:{
type:Array,
default:()=>{return []}
}
},
data() {
return {
};
},
mounted() {
this.$nextTick(()=>{
this.waterFall("#tabContainer", ".tab-item"); //實現瀑布流
})
},
methods: {
waterFall(
wrapIdName,
contentIdName,
columns = 5,
columnGap = 20,
rowGap = 20
) {
// 獲得內容可用寬度(去除滾動條寬度)
const wrapContentWidth =
document.querySelector(wrapIdName).offsetWidth;
// 間隔空白區域
const whiteArea = (columns - 1) * columnGap;
// 得到每列寬度(也即每項內容寬度)
const contentWidth = parseInt((wrapContentWidth - whiteArea) / columns);
// 得到內容項集合
const contentList = document.querySelectorAll(contentIdName);
// 成行內容項高度集合
const lineConentHeightList = [];
for (let i = 0; i < contentList.length; i++) {
// 動態設置內容項寬度
contentList[i].style.width = contentWidth + "px";
// 獲取內容項高度
const height = contentList[i].clientHeight;
if (i < columns) {
// 第一行按序布局
contentList[i].style.top = "0px";
contentList[i].style.left = contentWidth * i + columnGap * i + "px";
// 將行高push到數組
lineConentHeightList.push(height);
} else {
// 其他行
// 獲取數組最小的高度 和 對應索引
let minHeight = Math.min(...lineConentHeightList);
let index = lineConentHeightList.findIndex(
(listH) => listH === minHeight
);
contentList[i].style.top = minHeight + rowGap +"px";
contentList[i].style.left = (contentWidth + columnGap) * index + "px";
// 修改最小列的高度 最小列的高度 = 當前自己的高度 + 拼接過來的高度 + 行間距
lineConentHeightList[index] += height + rowGap;
}
}
},
},
};
</script>
<style scoped>
* {
margin: 0;
padding: 0;
}
/* 最外層大盒子 */
.tab-container {
padding-top: 20px;
position: relative;
}
/* 每個小盒子 */
.tab-container .tab-item {
position: absolute;
height: auto;
border: 1px solid #ccc;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
background: white;
/* 元素不能中斷顯示 */
break-inside: avoid;
text-align: center;
}
.tab-container .tab-item img {
width: 100%;
height: auto;
display: block;
}
</style>
在使用這個插件的時候,有兩個問題,就是因為內層是position: absolute;定位,不會撐開外部的p,會導致外層盒模型不好布局,還有就是頁面下拉懶加載,那要怎么辦呢?
這里我給出我的處理方法
<template>
<div>
<div class="list-box" @scroll="scrollFun">
<compList :pbList="pbList" ref="compList"></compList>
</div>
</div>
</template>
<script>
import compList from "@/pages/test/components/compList";
export default {
name:'testList',
components:{
compList
},
data() {
return {
//瀑布流數據
pbList: [
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
}
],
addList:[
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
}
],
bottomMain:true
};
},
methods:{
scrollFun(e) {
const offsetHeight= e.target.offsetHeight
const scrollHeight= e.target.scrollHeight
const scrollTop= e.target.scrollTop
if((scrollHeight - (offsetHeight+scrollTop)) < 10){
if(this.bottomMain){
this.bottomMain = false
this.addListDataFun()
}
}
},
addListDataFun(){
this.$Spin.show({
render: (h) => {
return h('div', [
h('Icon', {
'class': 'demo-spin-icon-load',
props: {
type: 'ios-loading',
size: 18
}
}),
h('div', '數據更新中...')
])
}
});
setTimeout(() => {
this.pbList = this.pbList.concat(this.addList)
this.bottomMain = true
this.$nextTick(()=>{
this.$refs.compList.waterFall("#tabContainer", ".tab-item")
this.$Spin.hide()
})
},1000)
}
}
};
</script>
<style scoped>
.list-box{
position: relative;
width: 100%;
height: calc(100vh - 240px);
background: white;
padding: 20px 30px 20px 20px;
margin-top: 20px;
box-sizing: border-box;
overflow: auto;
}
</style>
下拉的核心代碼為:
scrollFun(e) {
const offsetHeight= e.target.offsetHeight
const scrollHeight= e.target.scrollHeight
const scrollTop= e.target.scrollTop
if((scrollHeight - (offsetHeight+scrollTop)) < 10){
if(this.bottomMain){
this.bottomMain = false
this.addListDataFun()
}
}
},
addListDataFun(){
this.$Spin.show({
render: (h) => {
return h('div', [
h('Icon', {
'class': 'demo-spin-icon-load',
props: {
type: 'ios-loading',
size: 18
}
}),
h('div', '數據更新中...')
])
}
});
setTimeout(() => {
this.pbList = this.pbList.concat(this.addList)
this.bottomMain = true
this.$nextTick(()=>{
this.$refs.compList.waterFall("#tabContainer", ".tab-item")
this.$Spin.hide()
})
},1000)
}
這里使用的是iView-UI的全局加載事件,如果你要用其他的UI框架,也可以自行修改
讀到這里,這篇“怎么使用Vue實現一個圖片水平瀑布流插件”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。