前期准备
我们使用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>
核心环节
前期准备的工作已经做完了,接下来就是最重要的环节,流程就是:找到最小的盒子,就是把第二行的数据先放到最小盒子的下面,来维护一个数组,新的高度,然后找下一个最小的,以此类推,维护高度的这么一个流程,最后就形成瀑布流
首先我们定义一个维护高度的数组,将循环出来的高度也添加进去
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])