小程序实现瀑布流布局

104 阅读1分钟

小程序瀑布流

基于vue3的小程序瀑布流

<template>
  <view class="u-waterfall">
    <view id="u-left-column" class="u-column"
      ><slot name="left" :left-list="leftList"></slot
    ></view>
    <view id="u-right-column" class="u-column">
      <slot name="right" :right-list="rightList"></slot>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, watch, getCurrentInstance } from "vue";
const props = defineProps({
  flowList: {
    type: Array,
    default: () => [],
  },
  // 每次向结构插入数据的时间间隔,间隔越长,越能保证两列高度相近,但是对用户体验越不好
  addTime: {
    type: Number,
    default: 250,
  },
});
const leftList = ref([]);
const rightList = ref([]);
const tempList = ref([]);
const instance = getCurrentInstance();
const uGetRect = (id: string) => {
  return new Promise((resolve, reject) => {
    const query = uni.createSelectorQuery().in(instance);
    query
      .select(id)
      .boundingClientRect((data) => {
        resolve(data);
      })
      .exec();
  });
};
const splitData = async () => {
  if (!tempList.value.length) {
    return;
  }
  const leftRect: any = await uGetRect("#u-left-column");
  const rightRect: any = await uGetRect("#u-right-column");
  // 如果左边小于或等于右边,就添加到左边,否则添加到右边
  const item = tempList.value[0];
  // 解决多次快速上拉后,可能数据会乱的问题,因为经过上面的两个await节点查询阻塞一定时间,加上后面的定时器干扰
  // 数组可能变成[],导致此item值可能为undefined
  if (!item) {
    return;
  }
  if (leftRect.height < rightRect.height) {
    leftList.value.push(item);
  } else if (leftRect.height > rightRect.height) {
    rightList.value.push(item);
  } else {
    // 这里是为了保证第一和第二张添加时,左右都能有内容
    // 因为添加第一张,实际队列的高度可能还是0,这时需要根据队列元素长度判断下一个该放哪边
    if (leftList.value.length <= rightList.value.length) {
      leftList.value.push(item);
    } else {
      rightList.value.push(item);
    }
  }
  // 移除临时列表的第一项
  tempList.value.splice(0, 1);
  // 如果临时数组还有数据,继续循环
  if (tempList.value.length) {
    setTimeout(() => {
      splitData();
    }, props.addTime);
  }
};
watch(
  () => props.flowList,
  (nVal, oVal) => {
    if (!oVal) {
      oVal = [];
    }
    if (nVal.length <= oVal.length) {
      leftList.value = [];
      rightList.value = [];
    }
    const length = leftList.value.length + rightList.value.length;
    const diff = nVal.slice(length);
    //差值
    tempList.value = tempList.value.concat(JSON.parse(JSON.stringify(diff)));
    splitData();
  },
  {
    immediate: true,
  }
);
</script>

<style lang="scss" scoped>
.u-waterfall {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
}

.u-column {
  display: flex;
  flex: 1;
  flex-direction: column;
  height: auto;
}

.u-image {
  width: 100%;
}
</style>