前面寫過vuejs實現的瀑布流布局,《vuejs實現瀑布流布局(一)》和《vuejs實現瀑布流布局(二)》也確實實現了瀑布流布局,但是這個是基於SUI-Mobile實現的無限滾動。
近日稍有空閑,回頭重新實現了一下移動端的瀑布流布局,擺脫了移動端UI框架的束縛。
移動端的適配,采用的是adaptive-version2.js,而無限加載采用的是Vue Scroller。
最終實現的效果大致如下:

解決了無限滾動和移動端適配的問題,瀑布流也就只剩下一個難點,怎么樣讓高度不盡相同的圖片能夠按左右順序排列下來,還不至於錯亂太多,相差太大。
事實上css也已經為我們提供了解決方案,但是css的方案有一個巨大的缺陷在於:一旦圖片發生變動,不論增加還是減少,或者說圖片資源本身發生變化,他會將整體所有圖片的位置進行重排。就是說只要我們動了任意一張圖片,其布局可能會完全不同。
而js的瀑布流,主流方案似乎只有兩種(具體更多的方案沒有研究),一個利用絕對定位,一個分左右兩欄(或者多欄),每欄內部垂直布局,而依據圖片高度的不同,將其放入適合的欄目內,實現瀑布流。
我們這里采用的就是左右兩欄的布局。
<div class="waterfalls">
<ul>
<li class="photo" v-for="item in waterfallsLeft" :key="item.id + item.albumId+Math.random()">
<a href="javascrip:;">
<img :src="item.src" alt="">
</a>
<div class="desc-info">
<p>編號:<span>{{item.albumId}}</span></p>
</div>
<div class="thumbnail">
<div class="thumbnail-desc">
<p>{{item.title}}</p>
<span>{{item.thumbnailUrl}}</span>
</div>
<div class="praise active">
<div>
<i>贊</i>
<span>{{item.albumId}}</span>
</div>
</div>
</div>
</li>
</ul>
<ul>
<li class="photo" v-for="item in waterfallsRight" :key="item.id + item.albumId+Math.random()">
<a href="javascrip:;">
<img :src="item.src" alt="">
</a>
<div class="desc-info">
<p>編號:<span>00001</span></p>
</div>
<div class="thumbnail">
<div class="thumbnail-desc">
<p>雙語小學</p>
<span>詹天佑</span>
</div>
<div class="praise active">
<div>
<i>贊</i>
<span>92</span>
</div>
</div>
</div>
</li>
</ul>
</div>
div.waterfalls作為外層包裹,而里面的兩個ul,就是兩欄,li就是每一張圖片所對應的數據。ul寬度各占50%,然后浮動。剩下的就是怎么樣讓個張圖片自動歸位,進入waterfallsLeft和waterfallsRight。
首先請求數據:
getData(done){
let _this = this;
axios.get("https://jsonplaceholder.typicode.com/photos", {page: this.page})
.then(res=>res.data)
.then(res => {
let counts = res.slice((this.page-1) * this.pageCount, this.page * this.pageCount)
// console.log(counts.length)
counts.forEach(item => {
item.src ='http://cued.xunlei.com/demos/publ/img/P_'+ this.randomNum()+ '.jpg'
});
this.page++;
this.itemsLen = counts.length;
this.judgeAllLoaded(counts)
self.bottom = self.bottom + 10;
if(done) done();
})
},
randomNum(){ // 三位數隨機數,162以內
let random = Math.floor(Math.random() * 162)
return random = random < 10 ? '00'+random : random < 100 ? '0'+random : ''+random;
},
請求的是https://jsonplaceholder.typicode.com/photos,有很多數據可以進行一些模擬請求,但是其返回的圖片都是相同大小的,所以為其添加圖片資源,item.src ='http://cued.xunlei.com/demos/publ/img/P_'+ this.randomNum()+ '.jpg'。
有了數據,就得按照圖片在相同寬度的情況下,高度不同,將其分組分別進入左右兩欄this.judgeAllLoaded(counts)。
judgeAllLoaded(items){ // 判斷所有圖片是否加載完成
let _this = this;
items.forEach(item => {
let IMG = new Image();
IMG.src = item.src;
IMG.width = 100;
IMG.onload = function () {
_this.flag++;
if(_this.leftHeight <= _this.rightHeight){
_this.leftHeight += IMG.height;
_this.waterfallsLeft.push(item)
}else{
_this.rightHeight += IMG.height;
_this.waterfallsRight.push(item)
}
}
})
}
這里關鍵點在於:IMG.src = item.src; IMG.width = 100; IMG.onload = function;
為IMG對象添加src,並設置相同寬度100,然后等待圖片加載完成onload,在圖片加載完成之后,判斷左右兩欄,哪一欄高度較低,該條數據進入哪一欄,這里有一個不大不小的問題,就是由於不確定哪張圖片先加載完成,導致即使請求回來的數據相同,其排列的位置也未必相同,但是已經留下了解決flag作為可能的解決方案。就是每加載完成一張圖片,flag自增,監聽flag的變化,當flag的值與請求回來的數據相等時,再進行數據分配,理論上應該可以解決該問題,留待后面實現。
數據分配完成,其實瀑布流也已經實現了,剩下的就是無限滾動,繼續分配請求回來的數據了。
具體代碼可以查看GitHub上的GitProjectWaterfalls。
