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;

整个项目的案列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值