效果图:
image.png
实现思路:
核心是vue的nextTick配合vant中的list上拉加载组件实现;
这是两列,所以这里用了两个容器,也就是两个数组,一列为一个容器,然后往里push数据,在判断哪一个容器的高度小,就往哪一列push数据,页面初始化的时候,左侧容器的高度最低,然后往里添加了一条数据,这里就碰到了一个问题,由于实现逻辑是判断页面元素高度,第一次渲染完后,该怎么再次执行判断函数呢,这里就用到了vue的nextTick()将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上;
代码
<template>
<div>
<van-list
v-model="loading"
v-bind="$attrs"
@load="onLoad"
>
<div
class="waterfall-wrapper"
>
<ul
ref="leftWaterfall"
class="left-waterfall"
>
<li
v-for="(item,index) in leftItems"
:key="index"
>
<div
class="img-card"
>
<van-image
width="100%"
:src="item.img"
fit="fill"
/>
<div class="topping-tab">
置顶
</div>
<div class="right-btn">
发送
</div>
<p class="text-ellipsis img-card-name">
图片名称图片名称图{{ index }}
</p>
</div>
</li>
</ul>
<ul
ref="rightWaterfall"
class="right-waterfall"
>
<li
v-for="(item,index) in rightItems"
:key="index"
>
<div
class="img-card"
>
<van-image
width="100%"
:src="item.img"
fit="fill"
/>
<div class="topping-tab">
置顶
</div>
<div class="right-btn">
发送
</div>
<p class="text-ellipsis img-card-name">
图片名称图片名称图{{ index }}
</p>
</div>
</li>
</ul>
</div>
</van-list>
</div>
</template>
<script>
export default {
props: {
imgCardList: {
type: Array,
default: () => {
return []
}
}
},
data () {
return {
loading: false,
list: [],
leftItems: [],
rightItems: [],
imgCardListS: [],
waterfallFlage: false,
waterfalNum: 0
}
},
computed: {
},
created () {
},
mounted () {
},
methods: {
onLoad () {
this.$emit('onLoad')
},
updateWaterfall () {
const leftHeight = this.$refs.leftWaterfall.clientHeight
const rightHeight = this.$refs.rightWaterfall.clientHeight
if (this.waterfalNum !== this.imgCardList.length) {
this.waterfallFlage = false
}
if (!this.waterfallFlage) {
this.waterfallFlage = true
if (this.imgCardList.length !== 0) {
this.imgCardListS = this.$deepClone(this.imgCardList)
this.imgCardListS.splice(0, this.waterfalNum)
this.waterfalNum = this.imgCardList.length
} else {
this.imgCardListS = this.$deepClone(this.imgCardList)
this.waterfalNum = this.imgCardList.length
}
}
const item = this.imgCardListS.shift()
if (item == null) {
return
}
if (leftHeight <= rightHeight) {
this.leftItems.push(item)
} else {
this.rightItems.push(item)
}
this.$nextTick(() => {
this.updateWaterfall()
})
}
}
}
</script>
<style lang="scss" >
.waterfall-wrapper{
overflow: hidden;
ul{
width: 48%;
}
li{
width: 100%;
div{
width: 100%;
img{
width: 100%;
display: block;
min-height: 86px;
border-radius: 8px;
}
}
}
.left-waterfall{
float: left;
}
.right-waterfall {
float: right;
}
}
.box>div{
width: 99%;
margin-bottom: 20px;
}
.img-card{
position: relative;
font-size: 14px;
margin-bottom: 15px;
border-radius: 8px;
overflow: hidden;
.topping-tab{
position: absolute;
top: 8px;
left: 8px;
width: 40px;
height: 22px;
background:rgba(0, 0, 0, 0.6);
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #FFFFFF;
text-align: center;
line-height: 22px;
}
.right-btn{
width: 66px;
height: 28px;
position: absolute;
top: 8px;
right: 8px;
text-align: center;
line-height: 28px;
background: #006EF3;
border-radius: 14px;
font-weight: 400;
color: #FFFFFF;
line-height: 28px;
}
.img-card-name{
padding: 10px 8px;
position: absolute;
bottom: 0px;
color: #FFFFFF;
box-sizing: border-box;
margin: 0;
width: 100%;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 100%);
}
}
</style>