瀑布流,又稱瀑布流式佈局。是比較流行的一種網站頁面佈局,視覺表現爲良莠不齊的多欄佈局,隨着頁面滾動條向下滾動,這種佈局還會不斷加載數據塊並附加至當前尾部。最先採用此佈局的網站是Pinterest,逐漸在國內流行開來。國內大多數清新站基本爲這類風格。javascript
那麼瀑布流的規則是什麼哪?下面將用圖解的方式分析一下瀑布流的算法css
第一排圖片的頂部會處於同一個高度,依次排列在頂端,哪第一排排滿以後,後面的圖片,也就是第6張圖片應該怎樣排列? html
下面以Vue爲例來展現怎樣實現瀑布流效果java
佈局就比較簡單的寫一下哈~ 反正主要講原理~算法
.v-waterfall-content{
width: 100%;
height: 100%;
position: relative;
}
.v-waterfall-item{
position: absolute;
}
.v-waterfall-item img{
width: 100%;
height: 100%;
}
複製代碼
首先在data
中定義一些基本的數據數組
waterfallList:[],//存放計算好的數據
waterfallImgWidth:200,//每一列的寬度
waterfallImgCol:5,//多少列
waterfallImgRight:10,//右邊距
waterfallImgBottom:10,//下邊距
waterfallDeviationHeight:[],//存放瀑布流各個列的高度
imgList:[]
複製代碼
而後給圖片數組填充圖片數據:瀏覽器
let imgArr = [
require('../assets/100x70.png'),
require('../assets/100x80.png'),
require('../assets/100x90.png'),
require('../assets/100x100.png'),
require('../assets/100x120.png'),
require('../assets/100x150.png'),
require('../assets/100x210.png'),
require('../assets/100x230.png'),
require('../assets/100x250.png')
];
for (let i = 0;i < 100;i++){
this.imgList.push(this.imgArr[Math.round(Math.random() * 8)]);
}
複製代碼
這裏要注意,在js中存放靜態文件連接的時候要用require
,否則不會顯示:app
<div class="v-waterfall-content">
<div v-for="img in imgList" class="v-waterfall-item">
<img :src="img" alt="">
</div>
</div>
複製代碼
根據上面解釋的瀑布流的基本原理,就是須要找到圖片列裏面高度最低的那一個,要找到最低的就須要記錄沒一列的高度,下面是記錄列高度的實現:dom
//圖片預加載,獲取圖片寬和高
imgPreloading(){
for (let i = 0;i < this.imgList.length;i++){
let aImg = new Image();
aImg.src = this.imgList[i];
aImg.onload = aImg.onerror = (e)=>{
let imgData = {};
//根據設定的列寬度求出圖片的高度
imgData.height = this.waterfallImgWidth/aImg.width*aImg.height;
imgData.src = this.imgList[i];
this.waterfallList.push(imgData);
//調用圖片位置計算方法
this.rankImg(imgData);
}
}
}
//計算圖片偏移量
rankImg(imgData){
let {waterfallImgWidth,waterfallImgRight,waterfallImgBottom,waterfallDeviationHeight,waterfallImgCol} = this;
//找出當前最短列的索引
let minIndex = this.waterfallDeviationHeight.indexOf(Math.min.apply(null, this.waterfallDeviationHeight))
//獲取最短列底部高度,既下一張圖片的頂部高度
imgData.top = waterfallDeviationHeight[minIndex];
//計算左側偏移,最短列索引*(右邊距+列寬度)
imgData.left = minIndex*(waterfallImgRight+waterfallImgWidth);
//改變當前列高度
waterfallDeviationHeight[minIndex] += imgData.height + waterfallImgBottom;
}
複製代碼
這兩個方法就是瀑布流的核心代碼了,修改一下html代碼,在剛纔寫的給圖片數組填充完數據以後調用一下imgPreloading
方法,刷新瀏覽器就能夠看到效果啦!佈局
<div class="v-waterfall-content" id="v-waterfall">
<div v-for="img in waterfallList" class="v-waterfall-item" :style="{top:img.top+'px',left:img.left+'px',width:waterfallImgWidth+'px',height:img.height}">
<img :src="img.src" alt="">
</div>
</div>
複製代碼
<template>
<div class="v-waterfall-content" id="v-waterfall">
<div v-for="img in waterfallList"
class="v-waterfall-item"
:style="{top:img.top+'px',left:img.left+'px',width:waterfallImgWidth+'px',height:img.height}">
<img :src="img.src" alt="">
</div>
</div>
</template>
<script>
export default {
name: "v-waterfall",
data(){
return {
waterfallList:[],
imgArr:[
require('../assets/100x70.png'),
require('../assets/100x80.png'),
require('../assets/100x90.png'),
require('../assets/100x100.png'),
require('../assets/100x120.png'),
require('../assets/100x150.png'),
require('../assets/100x210.png'),
require('../assets/100x230.png'),
require('../assets/100x250.png')
],
waterfallImgWidth:100,
waterfallImgCol:5,
waterfallImgRight:10,
waterfallImgBottom:10,
waterfallDeviationHeight:[],
imgList:[]
}
},
created() {
for (let i = 0;i < 100;i++){
this.imgList.push(this.imgArr[Math.round(Math.random() * 8)]);
}
},
mounted(){
this.calculationWidth();
},
methods:{
//計算每一個圖片的寬度或者是列數
calculationWidth(){
let domWidth = document.getElementById("v-waterfall").offsetWidth;
if (!this.waterfallImgWidth && this.waterfallImgCol){
this.waterfallImgWidth = (domWidth-this.waterfallImgRight*this.waterfallImgCol)/this.waterfallImgCol;
}else if(this.waterfallImgWidth && !this.waterfallImgCol){
this.waterfallImgCol = Math.floor(domWidth/(this.waterfallImgWidth+this.waterfallImgRight))
}
//初始化偏移高度數組
this.waterfallDeviationHeight = new Array(this.waterfallImgCol);
for (let i = 0;i < this.waterfallDeviationHeight.length;i++){
this.waterfallDeviationHeight[i] = 0;
}
this.imgPreloading()
},
//圖片預加載
imgPreloading(){
for (let i = 0;i < this.imgList.length;i++){
let aImg = new Image();
aImg.src = this.imgList[i];
aImg.onload = aImg.onerror = (e)=>{
let imgData = {};
imgData.height = this.waterfallImgWidth/aImg.width*aImg.height;
imgData.src = this.imgList[i];
this.waterfallList.push(imgData);
this.rankImg(imgData);
}
}
},
//瀑布流佈局
rankImg(imgData){
let {waterfallImgWidth,waterfallImgRight,waterfallImgBottom,waterfallDeviationHeight,waterfallImgCol} = this;
//for (let i = 0;i < this.waterfallList.length;i++){
let minIndex = this.filterMin();
imgData.top = waterfallDeviationHeight[minIndex];
imgData.left = minIndex*(waterfallImgRight+waterfallImgWidth);
waterfallDeviationHeight[minIndex] += imgData.height + waterfallImgBottom;
//}
console.log(imgData);
},
/**
* 找到最短的列並返回下標
* @returns {number} 下標
*/
filterMin(){
const min = Math.min.apply(null, this.waterfallDeviationHeight);
return this.waterfallDeviationHeight.indexOf(min);
}
}
}
</script>
<style scoped>
.v-waterfall-content{
width: 100%;
height: 100%;
position: relative;
}
.v-waterfall-item{
float: left;
position: absolute;
}
.v-waterfall-item img{
width: auto;
height: auto;
}
</style>
複製代碼