vue實現瀑布流

FrancisXu發表於2019-04-01

一開始覺得這個問題很簡單,每插入一張圖片之前比較各個“流”的高度,找個最短的放進去就行了。後來發現沒那麼簡單。 直接想到的問題就是圖片未載入完成是得不到圖片的完整高度的,後來找到dom的naturalHeight屬性,可以在圖片未載入成功時就獲取到圖片高度

然後開始常規操作,用nextTick遞迴把圖片資料插入到對應的陣列中,但是nextTick中獲取圖片dom的naturalHeight是獲取不到的,但是頁面中圖片就算未載入完成是可以獲取到的。猜想是vue渲染完圖片的dom時執行nextTick,圖片剛開始載入,這時候介面還沒有獲取到計算naturalHeight屬性相關的資料。所以這條路就是走死了,因為這個方法下圖片什麼時候載入完成無法監控,都是第一張圖片插入之後第二張圖片放哪裡沒法計算。

第一時間想到的就是setInterval,然後不到一秒鐘就被自己否決了,醜而且耗費效能,寧願在資料庫裡擴充套件width、height欄位也不用這個方法(我還真的擴充套件了)。但是不太服氣,然後尋找滿是廣告的度娘,發現一個方法,既然之前問題出在圖片什麼時候載入完成無法監控,那就用image.onload()方法獲取圖片資訊,然後在回撥裡面丁零當啷一頓操作拼好dom,插入到頁面中。 偷來的程式碼

sort(j) {
    if (j < this.moments.length) {
        let that = this;
        // 建立Image類
        var newImg = new Image();
        // 獲取要載入的圖片地址
        newImg.src =
            "http://lanyue.ink:8123/images/" +
            (Math.floor(Math.random() * 15) + 1) +
            ".png";
        // 圖片載入完成後(非同步)
        newImg.onload = () => {
            // 四個管道的高度 
            var arr = [
                that.$refs.piping0.offsetHeight,
                that.$refs.piping1.offsetHeight,
                that.$refs.piping2.offsetHeight,
                that.$refs.piping3.offsetHeight
            ];
            //獲取管道最小高度
            var min = arr.indexOf(Math.min.apply(Math, arr));
            // 新增卡片的模板
            var html =
                `<div class="card">
                    <img src=` + newImg.src + `>
                        <div>
                            <img src="http://lanyue.ink:8123/images/avatar.jpg" alt="">
                            <div>` + this.moments[j].id + "  " + this.moments[j].content + `</div>
                        </div>
                    </div>`;
            //給最小的管道新增卡片
            if (min == 0) {
                that.$refs.piping0.innerHTML += html;
            } else if (min == 1) {
                that.$refs.piping1.innerHTML += html;
            } else if (min == 2) {
                that.$refs.piping2.innerHTML += html;
            } else if (min == 3) {
                that.$refs.piping3.innerHTML += html;
            }
            that.sort(j + 1);
        };
    }
},
複製程式碼

作者如果看到了不要噴我,都走到這一步了,為什麼還要用插入html的方式,拿到圖片的寬高,加一個計算“流”高度的欄位,插入一張圖就加一個圖片的高度,這樣也就不用再從dom中獲取“流”的高度。這樣的好處有兩個,一是減少一次dom的查詢,二是如果頁面需要響應式,插入html的方式其實是無法通過直接改變資料操作dom,違背了vue的本意(對於兩個方法的效能消耗有機會多做研究,感覺起來應該是我的方法美一點)。

getImageList() {
    let that = this;
    imageService.getImageList(this, {
        params: {
            categoryId: 37
        }
    }).then(function (result) {
        if (result.code === 0) {
            that.tempImage = result.data;
            that.pushImage(0);
        }
    });
},

pushImage(index) {
    if (index >= this.tempImage.length) return;
    let img = new Image(), that = this;
    img.src = that.$store.state.imageURL + that.tempImage[index].url;
    img.onload = () => {
        let min = that.imageHeight[0], imageIndex = 0;
        that.imageHeight.forEach(function (item, _index) {
            if (min > item) {
                min = item;
                imageIndex = _index;
            }
        });
        that.imageHeight[imageIndex] += img.naturalHeight / img.naturalWidth;
        that.imageList[imageIndex].push(that.tempImage[index]);
        that.pushImage(index + 1);
    }
},
複製程式碼

最後再加上一段監控頁面位置函式,實現拉到底部載入圖片的功能

pullDown() {
    // 獲取滾輪位置
    this.height1 = window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop;
    // 文件高度
    this.height2 = document.body.scrollHeight;
    // 可視區域
    // 設定compatMode相容IE
    this.height3 = document.compatMode === "CSS1Compat"
        ? document.documentElement.clientHeight
        : document.body.clientHeight;
    // 如果滾動到最低(這裡設定離最底還有100距離才觸發函式)
    // available條件是為了防止觸底時一直不斷地請求。因此,請求一次後available設為0,直到滾動到離底部超過100距離(即資料載入玩後)才設為1
    if (this.height3 + this.height1 >= this.height2 - 100 && this.available) {
        this.available = false;
        //請求下一頁,如果當前頁等於最大頁,直接返回
        if (this.pagination.currentPage >= this.pagination.totalPage) {
            this.footerVisible = true;
            return;
        }
        this.pagination.currentPage++;
        this.getImagePage();
    } else if (this.height3 + this.height1 < this.height2 - 100) {
        this.available = true;
    }
}

複製程式碼

最終效果圖

vue實現瀑布流

參考文章:來自MarieDreamer blog.csdn.net/qq_33514421…

相關文章