Vue3封装瀑布流组件

514 阅读2分钟

前期准备

我们使用div盒子来代替图片,自己先造一些假数据,高度不同,颜色不同的数据;父组件准备好的数据传给子组件,子组件通过defineProps接收

//父组件
Water :list="list"></Water>
<style lang="scss">
#app,
html,
body {
    height: 100%;
}
 
* {
    padding: 0;
    margin: 0;
}
</style>
//子组件
const props = defineProps<{
  list:any[]
}>()

第一步需要操作dom,使用onMounted中触发定义的函数,先获取可视区域宽度通过clientWidth获取,在这个可视区域摆多少列,将获取的可视区域宽度除以盒子的宽度,在通过Math.floor向下取正

onMounted(() => {
  init()
})
const init =() => {
  const width = 130;
  const x = document.body.clientWidth;
  const column  = Math.floor(x / width)
}

有了这个数据我们就去for遍历,先渲染第一行的数据,定义一个响应式的新数组变量waterList,在循环中我们添加left、top的变量,left的偏移量i * width去定义,top就先定义20

const waterList = reactive<any[]>([]);
  for(let i = 0; i < props.list.length; i++) {
     if(i < column) {
      props.list[i].left = i * width;
      props.list[i].top = 20;
      waterList.push(props.list[i])
     }
  }

拿着waterList变量数据就可以去渲染dom,动态去添加style的值,就这样第一行的数据就显示出来了

  <div class="items" :style="{height: item.height + 'px', background: item.background,left: item.left + 'px', top: item.top + 'px'}" v-for="item in waterList"></div>
</div>

91cdaf8610b563233d5dae211bdde43.png

核心环节

前期准备的工作已经做完了,接下来就是最重要的环节,流程就是:找到最小的盒子,就是把第二行的数据先放到最小盒子的下面,来维护一个数组,新的高度,然后找下一个最小的,以此类推,维护高度的这么一个流程,最后就形成瀑布流

首先我们定义一个维护高度的数组,将循环出来的高度也添加进去

const heightList:number[] = [] ;
heightList.push(props.list[i].height)

我们假装数组的第一个是最小的,再定义一个索引,方便控制他,循环维护高度的数组,拿第一个的高度去判断他后面,最后就获取的最小的

      let current = heightList[0];
      let index = 0;
      heightList.forEach((h, i) => {
        if(current > h) {
          current = h;
          index = i
        }
      })

维护的数组高度添加到waterList数组,注意的是height会叠加的一块,需重新定义原先的高度

      props.list[i].top = current + 20
      props.list[i].left = index * width
      heightList[index] = heightList[index] + props.list[i].height + 20
      waterList.push(props.list[i])

呈现出的结果

323e8da1be070a25bea3a4b9fbcc303.png