您好,登錄后才能下訂單哦!
本文小編為大家詳細(xì)介紹“怎么使用Vue實(shí)現(xiàn)一個(gè)圖片水平瀑布流插件”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“怎么使用Vue實(shí)現(xiàn)一個(gè)圖片水平瀑布流插件”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。
項(xiàng)目主體用的是之前在學(xué)習(xí)的CRMEB的后端框架來(lái)開(kāi)發(fā),UI使用iView-UI,其余的場(chǎng)景與其他的vue項(xiàng)目一致。
如果不是vue環(huán)境,我們的邏輯為
1.獲取所有的p元素
2.獲取盒子的寬度,寬度都是相同,高度不同
3.在浮動(dòng)布局中每一行的盒子個(gè)數(shù)不固定,是根據(jù)屏幕寬度和盒子寬度決定
4.獲取屏幕寬度
5.求出列數(shù),屏幕寬度 / 盒子寬度 取整
6.瀑布流最關(guān)鍵的是第二行的盒子的排布方式,通過(guò)獲取第一行盒子中最矮的一個(gè)的下標(biāo),絕對(duì)定位,top是最矮盒子的高度,left是最矮盒子的下標(biāo) * 盒子的寬度
7.循環(huán)遍歷所有的盒子,通過(guò)列數(shù)找到第一行所有的盒子,將第一行盒子的高度放入數(shù)組,再取出數(shù)組中最小的一個(gè)的下標(biāo),就是第6步思路的第一行盒子中最矮盒子的下標(biāo)。
8.循環(huán)繼續(xù),第二行第一個(gè)盒子,通過(guò)絕對(duì)定位,放進(jìn)頁(yè)面。
9.關(guān)鍵,需要將數(shù)組中最小的值加上放進(jìn)的盒子的高度
10.繼續(xù)循環(huán),遍歷所有
11.如果想要加載更多,需要判斷最后一個(gè)盒子的高度和頁(yè)面滾動(dòng)的距離,再將數(shù)據(jù)通過(guò)創(chuàng)建元素,追加進(jìn)頁(yè)面,再通過(guò)瀑布流布局展示
但如果是Vue項(xiàng)目,我們可以把邏輯歸結(jié)為以下幾步
1.獲取屏幕寬度
2..獲取盒子的寬度,寬度都是相同,高度不同
3.在浮動(dòng)布局中每一行的盒子個(gè)數(shù)不固定,是根據(jù)屏幕寬度和盒子寬度決定
4.求出列數(shù),屏幕寬度 / 盒子寬度 取整
5.瀑布流最關(guān)鍵的是第二行的盒子的排布方式,通過(guò)獲取第一行盒子中最矮的一個(gè)的下標(biāo),絕對(duì)定位,top是最矮盒子的高度,left是最矮盒子的下標(biāo) * 盒子的寬度
6.繼續(xù)循環(huán),遍歷所有
7.如果想要加載更多,需要判斷最后一個(gè)盒子的高度和頁(yè)面滾動(dòng)的距離,再將數(shù)據(jù)通過(guò)創(chuàng)建元素,追加進(jìn)頁(yè)面,再通過(guò)瀑布流布局展示
先看下我的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;
}
/* 每個(gè)小盒子 */
.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"); //實(shí)現(xiàn)瀑布流
})
},
methods: {
waterFall(
wrapIdName,
contentIdName,
columns = 5,
columnGap = 20,
rowGap = 20
) {
// 獲得內(nèi)容可用寬度(去除滾動(dòng)條寬度)
const wrapContentWidth =
document.querySelector(wrapIdName).offsetWidth;
// 間隔空白區(qū)域
const whiteArea = (columns - 1) * columnGap;
// 得到每列寬度(也即每項(xiàng)內(nèi)容寬度)
const contentWidth = parseInt((wrapContentWidth - whiteArea) / columns);
// 得到內(nèi)容項(xiàng)集合
const contentList = document.querySelectorAll(contentIdName);
// 成行內(nèi)容項(xiàng)高度集合
const lineConentHeightList = [];
for (let i = 0; i < contentList.length; i++) {
// 動(dòng)態(tài)設(shè)置內(nèi)容項(xiàng)寬度
contentList[i].style.width = contentWidth + "px";
// 獲取內(nèi)容項(xiàng)高度
const height = contentList[i].clientHeight;
if (i < columns) {
// 第一行按序布局
contentList[i].style.top = "0px";
contentList[i].style.left = contentWidth * i + columnGap * i + "px";
// 將行高push到數(shù)組
lineConentHeightList.push(height);
} else {
// 其他行
// 獲取數(shù)組最小的高度 和 對(duì)應(yīng)索引
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";
// 修改最小列的高度 最小列的高度 = 當(dāng)前自己的高度 + 拼接過(guò)來(lái)的高度 + 行間距
lineConentHeightList[index] += height + rowGap;
}
}
},
},
};
</script>
這里要給大家提個(gè)醒,在當(dāng)插件使用的時(shí)候,我們需要用this.$nextTick()來(lái)進(jìn)行頁(yè)面初始化,因?yàn)榉椒ǔ晒Φ那疤崾且软?yè)面初始化加載完畢后才能進(jìn)行獲取和計(jì)算
<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"); //實(shí)現(xiàn)瀑布流
})
},
methods: {
waterFall(
wrapIdName,
contentIdName,
columns = 5,
columnGap = 20,
rowGap = 20
) {
// 獲得內(nèi)容可用寬度(去除滾動(dòng)條寬度)
const wrapContentWidth =
document.querySelector(wrapIdName).offsetWidth;
// 間隔空白區(qū)域
const whiteArea = (columns - 1) * columnGap;
// 得到每列寬度(也即每項(xiàng)內(nèi)容寬度)
const contentWidth = parseInt((wrapContentWidth - whiteArea) / columns);
// 得到內(nèi)容項(xiàng)集合
const contentList = document.querySelectorAll(contentIdName);
// 成行內(nèi)容項(xiàng)高度集合
const lineConentHeightList = [];
for (let i = 0; i < contentList.length; i++) {
// 動(dòng)態(tài)設(shè)置內(nèi)容項(xiàng)寬度
contentList[i].style.width = contentWidth + "px";
// 獲取內(nèi)容項(xiàng)高度
const height = contentList[i].clientHeight;
if (i < columns) {
// 第一行按序布局
contentList[i].style.top = "0px";
contentList[i].style.left = contentWidth * i + columnGap * i + "px";
// 將行高push到數(shù)組
lineConentHeightList.push(height);
} else {
// 其他行
// 獲取數(shù)組最小的高度 和 對(duì)應(yīng)索引
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";
// 修改最小列的高度 最小列的高度 = 當(dāng)前自己的高度 + 拼接過(guò)來(lái)的高度 + 行間距
lineConentHeightList[index] += height + rowGap;
}
}
},
},
};
</script>
<style scoped>
* {
margin: 0;
padding: 0;
}
/* 最外層大盒子 */
.tab-container {
padding-top: 20px;
position: relative;
}
/* 每個(gè)小盒子 */
.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>
在使用這個(gè)插件的時(shí)候,有兩個(gè)問(wèn)題,就是因?yàn)閮?nèi)層是position: absolute;定位,不會(huì)撐開(kāi)外部的p,會(huì)導(dǎo)致外層盒模型不好布局,還有就是頁(yè)面下拉懶加載,那要怎么辦呢?
這里我給出我的處理方法
<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 {
//瀑布流數(shù)據(jù)
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', '數(shù)據(jù)更新中...')
])
}
});
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', '數(shù)據(jù)更新中...')
])
}
});
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實(shí)現(xiàn)一個(gè)圖片水平瀑布流插件”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。