瀑布流计算js源码

遇到问题时的解决方法及使用注意事项:

1、下拉刷新会导致瀑布流顺序错乱,重新new一下瀑布流即可。

2、图片宽高必须要让服务端返,前端去获取数据太多会导致性能非常慢。

3、dom加载时获取小程序的某个dom的宽度(列宽)为空,放在wx.nextTick中即可。还获取不到,就需要看自己要获取的dom是否已加载。

4、此代码抗揍,如果出问题先从自身找问题,再仔细阅读一遍此代码。

class Waterfall {
  constructor(options) {
    this.initWaterfall(options);
  }
    /**
   * 初始化瀑布流
   * @param {Object} options
   * @param {Array<Object>} options.columns {width} - 此参数主要定义每列的宽度
   */
  initWaterfall(options) {
    this.columns = options.columns;
    this.formatedData = [];
    for (let column of options.columns) {
      this.formatedData.push({
        width: column.width,
        height: 0
      });
    }
  }
  imageUrlFeild = 'imageUrl'
 
  /**
   * 格式化从数据中获取图片url的方法
   * @param {Function | String | Number} fn - 若为函数,则需要返回瀑布流每项数据对应的图片字段
   */
 
  formatGetImageUrlMethod(fn) {
    this.imageUrlFeild = typeof fn === 'function' ? fn() : fn
  }
 
  /**
   * 向瀑布流中填充数据
   * @param {Object}  resource - 单个瀑布流项的数据源
   */
  getResourceInfo(resource) {
    const _self = this
    return new Promise(resolve => {
      if (resource[_self.imageUrlFeild]) {
        // getImageInfo方法在小程序中使用,h5中暂未测试,不过一般都是让服务端直接返回图片宽高,所以这个方法一般用不到,如果需要用到的话,必须加一层loading阻止用户操作
        wx.getImageInfo({
          src: resource[_self.imageUrlFeild],
          success(res) {
            resolve(res)
          },
          fail() {
            resolve()
          }
        })
      } else {
        resolve()
      }
    })
  }
 
  /**
   * 向瀑布流中补充数据
   * @param {Boolean} extraVal 瀑布流的卡片中图片之外的内容
   * @param {Array<resource>}  resources
   */
 
  async addResources(resources, extraVal) {
    // let promiseArray = []
    let newResourcesArray = Array.apply(null, Array(this.columns.length)).map(
      _ => []
    )
    for (let resource of resources) {
      // promiseArray.push(this.getResourceInfo(resource))
      // let imageInfo
      // imageInfo = await this.getResourceInfo(resource)
      const width = Number(resource.width) || 100;
      const height = Number(resource.height) || 100;
      // 获取最短列的索引
      const index = this.getShortestColumn();
      // 卡片宽度
      const imageWidth = this.formatedData[index].width;
      // 附加的卡片高度在这里是写死的,每个项目会有不同高度
      const extraHeight = extraVal ? extraVal : 0;
      // 获取卡片高度
      const cardHeight = imageWidth / (width / height) + extraHeight;
      // 当前列总高度
      this.formatedData[index].height += cardHeight;
      // 返回卡片高度
      resource.cardHeight = cardHeight;
      // 返回图片高度
      resource.computedHeight = cardHeight - extraVal;
      // 返回当前列的数组
      newResourcesArray[index].push(resource);
    }
    return newResourcesArray
  }
 
  /**
   * 最重要的地方
   * 获取当前数据中填充最短的列
   * 返回最短列的索引
   */
  getShortestColumn() {
    let minHeight = Infinity
    let minHeightColumnIndex = 0
    for (let index = this.formatedData.length - 1; index > -1; index--) {
      //从后往前遍历,防止每列的高度相同的情况
      const height = this.formatedData[index].height
      if (height <= minHeight) {
        minHeightColumnIndex = index
        minHeight = height
      }
    }
    return minHeightColumnIndex
  }
}
 
export default Waterfall;
 
 
// 例:双列瀑布流
let newList = new Waterfall({
      columns: [
        {
          width: 列宽
        },
        {
          width: 列宽
        }
      ]
    });
newList.addResources(lists, 卡片内除图片的额外高度).then(res => {});