vue瀑布流類封裝

瀑布流效果類封裝

前言:瀑布流,隨着瀏覽器滾動,頁面小單元逐漸出現,且位置是不規則的出現,常用在商城網站,圖片類網站(花瓣網)等,十分美觀。

1.瀑布流思路

1.1 確定頁面可以分多少欄目

根據主體區域的寬度/第一個小塊的寬度,向下取整獲取可以分多少欄目
在這裏插入圖片描述


let { items, gap,dom } = this; //gap 間距 動態傳入
let oBoxWidth = dom.offsetWidth; //最外層box寬度
let itemWidth = items[0].offsetWidth; //子項
let colums = Math.floor(oBoxWidth / (itemWidth + gap)); //獲取行數目

1.2 加載的時候,如何把圖片插入到上一行高度最低的那一行

瀑布流最大的思路是如何確定好小塊的排序位置,一個數據列表如何按照規則往下排。
使用 Math.min.apply(null,arr) 確定最小索引。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳最小高度

let { items, gap,dom } = this;
let oBoxWidth = dom.offsetWidth;
let itemWidth = items[0].offsetWidth;
let colums = Math.floor(oBoxWidth / (itemWidth + gap)); //獲取行數目
let arr = [], //新建數組和當前最小下標
    minIndex;
for (let i = 0; i < items.length; i++) {
    //第一行正常排序
    if (i < colums) {
    items[i].style.top = 0;
    items[i].style.left = i * (itemWidth + gap) + "px";

    arr.push(items[i].offsetHeight);
    } else {
    //第一行以後
    minIndex = this.getMinIndex(arr); //注意
    items[i].style.left = items[minIndex].offsetLeft + "px";
    items[i].style.top = arr[minIndex] + gap + "px";
    arr[minIndex] = arr[minIndex] + items[i].offsetHeight + gap;
    }
}


getMinIndex() {
    let minHeight = Math.min.apply(null, arr);
    return arr.findIndex(function (item) {
      return item === minHeight;
    });
}

1.3 頁面滾動到底部加載下一頁數據,執行回調函數

封裝的該類,接收一個回調函數,當確定要滾動下一頁數據時觸發。
條件爲: (頁面高度+頁面滾動條高度>=最後一元素的offsetTop)
滾動條件

class waterFull {
    constructor({ dom = null, gap = 10, canScrollFn = () => { } }) {
    this.dom = dom;
    this.gap = gap;
    this.items = this.dom.children;
  }
   scrollFn() {
    let { items } = this;
    let canscroll =
      this.getScroll() + this.getClient().height >=
      items[items.length - 1].offsetTop;

    if (canscroll) {

      this.canScrollFn()
    }
  }

  getClient() {
    return {
      width:
        document.body.clientWidth ||
        document.documentElement.clientWidth ||
        window.innerWidth,
      height:
        document.body.clientHeight ||
        document.documentElement.clientHeight ||
        window.innerHeight
    };
  }
  getScroll() {
    return window.pageYOffset || document.documentElement.scrollTop;
  }
}

2.問題,頁面初始化,拿不到圖片的高度問題。

經過以上分析和調整基本可以實現一個瀑布流類。

class WaterFull {
  constructor({ dom = null, gap = 10, canScrollFn = () => { } }) {
    this.dom = dom;
    this.gap = gap;
    this.items = this.dom.children;
   
  
    this.canScrollFn = canScrollFn;
    this.init()
    this.wScroll();
    this.wResize();
  }
  init() {
    this.waterInit();
  }
  wResize() {
    let that = this;
    window.onresize = function () {
      that.init()
    };
  }
  wScroll() {
    
    let that = this;
    window.onscroll = throttle(function(){
      that.scrollFn()
    },100)
  }

  waterInit() {

    let { items, gap,dom } = this;
    let oBoxWidth = dom.offsetWidth;
    let itemWidth = items[0].offsetWidth;
    let colums = Math.floor(oBoxWidth / (itemWidth + gap)); //獲取行數目
    let arr = [],
      minIndex;
    for (let i = 0; i < items.length; i++) {
      //第一行
      if (i < colums) {
        items[i].style.top = 0;
        items[i].style.left = i * (itemWidth + gap) + "px";
        
        arr.push(items[i].offsetHeight);
      } else {
        //第一行以後
        minIndex = this.getMinIndex(arr);
        items[i].style.left = items[minIndex].offsetLeft + "px";
        items[i].style.top = arr[minIndex] + gap + "px";
        arr[minIndex] = arr[minIndex] + items[i].offsetHeight + gap;
      }
    }

  }
 
  scrollFn() {
    let { items } = this;
    let canscroll =
      this.getScroll() + this.getClient().height >=
      items[items.length - 1].offsetTop;

    if (canscroll) {
      this.canScrollFn()
    }
  }

  getMinIndex(arr) {
    let minHeight = Math.min.apply(null, arr);
    return arr.findIndex(function (item) {
      return item === minHeight;
    });
  }
  getClient() {
    return {
      width:
        document.body.clientWidth ||
        document.documentElement.clientWidth ||
        window.innerWidth,
      height:
        document.body.clientHeight ||
        document.documentElement.clientHeight ||
        window.innerHeight
    };
  }
  getScroll() {
    return window.pageYOffset || document.documentElement.scrollTop;
  }
}

但隨之而來的是,當我們在vue裏面初始話時候,函數不執行(圖片堆疊在一起,沒有正確的分欄),經調試發現每一個box有寬度沒有高度,所以沒每一個box無法正確的定位。

2.1vue在mounted時候,無法獲取圖片的正常高度。

渲染時是同步渲染的,所以只有圖片onload時候纔會有高度,但是一個頁面有很多圖片不可能每一個onload都觸發init函數,這個時候可以利用一個閉包來合併執行,不需要多次觸發,收集所有的觸發條件,歸爲依次觸發即可

let proxySync = (function() {
    let cache = [], // 保存一段時間內需要同步的 ID
        timer; // 定時器
    return function(id) {
        cache.push(id);
        if (timer) {
            // 保證不會覆蓋已經啓動的定時器
            return;
        }
        timer = setTimeout(function() {
            callBack.apply(this,arguments) //回調擺正this
            clearTimeout(timer); // 清空定時器
            timer = null;
            cache.length = 0; // 清空 ID 集合
        }, 500);
    };
})();

3.優化加載問題

爲了避免頁面滾動觸發太過於頻繁,可以使用節流函數優化scrool,關於節流的原理和實現,大家可以參考
節流

最終得到的結果


var throttle = function (callBack, time=500) {
  var prev = Date.now(),
      first = true;
  return function () {
      if (first) {
          callBack.apply(this, arguments);
          return first = false;
      }
      var now = Date.now();
      if (now - prev >= time) {
          callBack.apply(this, arguments);
          prev = Date.now();
      }
  }
}
/**
 * dom 最外層的box gap 間距 canScrollFn 滑到最底部的回調
 */

class WaterFull {
  constructor({ dom = null, gap = 10, canScrollFn = () => { } }) {
    this.dom = dom;
    this.gap = gap;
    this.items = this.dom.children;
   
  
    this.canScrollFn = canScrollFn;
    this.init()
    this.wScroll();
    this.wResize();
  }
  init() {
    this.waterInit();
  }
  wResize() {
    let that = this;
    window.onresize = function () {
      that.init()
    };
  }
  wScroll() {
    
    let that = this;
    window.onscroll = throttle(function(){
      that.scrollFn()
    },100)
  }

  waterInit() {

    let { items, gap,dom } = this;
    let oBoxWidth = dom.offsetWidth;
    let itemWidth = items[0].offsetWidth;
    let colums = Math.floor(oBoxWidth / (itemWidth + gap)); //獲取行數目
    let arr = [],
      minIndex;
    for (let i = 0; i < items.length; i++) {
      //第一行
      if (i < colums) {
        items[i].style.top = 0;
        items[i].style.left = i * (itemWidth + gap) + "px";
        
        arr.push(items[i].offsetHeight);
      } else {
        //第一行以後
        minIndex = this.getMinIndex(arr);
        items[i].style.left = items[minIndex].offsetLeft + "px";
        items[i].style.top = arr[minIndex] + gap + "px";
        arr[minIndex] = arr[minIndex] + items[i].offsetHeight + gap;
      }
    }

  }
 
  scrollFn() {
    let { items } = this;
    let canscroll =
      this.getScroll() + this.getClient().height >=
      items[items.length - 1].offsetTop;

    if (canscroll) {
      this.canScrollFn()
    }
  }

  getMinIndex(arr) {
    let minHeight = Math.min.apply(null, arr);
    return arr.findIndex(function (item) {
      return item === minHeight;
    });
  }
  getClient() {
    return {
      width:
        document.body.clientWidth ||
        document.documentElement.clientWidth ||
        window.innerWidth,
      height:
        document.body.clientHeight ||
        document.documentElement.clientHeight ||
        window.innerHeight
    };
  }
  getScroll() {
    return window.pageYOffset || document.documentElement.scrollTop;
  }
}
export default WaterFull;

整個項目的案列

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章