淺談瀑布流原理及Vue實現

瀑布流,又稱瀑布流式佈局。是比較流行的一種網站頁面佈局,視覺表現爲良莠不齊的多欄佈局,隨着頁面滾動條向下滾動,這種佈局還會不斷加載數據塊並附加至當前尾部。最先採用此佈局的網站是Pinterest,逐漸在國內流行開來。國內大多數清新站基本爲這類風格。javascript

瀑布流佈局效果

在這裏插入圖片描述
既多行等寬元素排列,等寬不等高,後面的元素依次排列在上一個元素後面

那麼瀑布流的規則是什麼哪?下面將用圖解的方式分析一下瀑布流的算法css

圖解瀑布流

第一排圖片的頂部會處於同一個高度,依次排列在頂端,哪第一排排滿以後,後面的圖片,也就是第6張圖片應該怎樣排列? html

在這裏插入圖片描述
是這樣嗎?
在這裏插入圖片描述
可能有人認爲會是這種方式進行排列,然而這樣排列的方式是錯誤的,這樣排列容易致使某一列可能會過長,而其餘的列會太短,致使列之間高度相差過大的狀況出現。那麼怎樣取解決這個問題那?答案就是利用 定位在最短的一列下面進行排列,就像下圖
在這裏插入圖片描述
看到這裏,基本上已經理解了瀑布流的基本原理,接下來就是怎樣去實現了!

瀑布流的實現

下面以Vue爲例來展現怎樣實現瀑布流效果java

1.基本的佈局

佈局就比較簡單的寫一下哈~ 反正主要講原理~算法

.v-waterfall-content{
    width: 100%;
    height: 100%;
    position: relative;
}
.v-waterfall-item{
    position: absolute;
}
.v-waterfall-item img{
    width: 100%;
    height: 100%;
}
複製代碼

2.瀑布流核心實現

首先在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>

複製代碼
相關文章
相關標籤/搜索