注册
web

使用flex实现瀑布流

什么是瀑布流


瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。


特点:



  • 固定宽度,高度不一
  • 参差不齐的布局

使用flex实现瀑布流


实现的效果是分成两列的瀑布流,往下滑动会加载下一页的数据,并渲染到页面中!


微信图片_20230731231617.jpg


样式的实现


<view class="blessing-con">
<view class='blessing-con-half'>
<view id="leftHalf">
<view class="blessing-con-half-item" :class="bgColor[index % 3]"
v-for="(item, index) in newBlessingWordsList1" :key="index">
<view class="item-con">
</view>
</view>
</view>
</view>
<view class='blessing-con-half'>
<view id="rightHalf">
<view class="blessing-con-half-item" :class="bgColor[(index + 1) % 3]"
v-for="(item, index) in newBlessingWordsList2" :key="index">
<view class="item-con"></view>
</view>
</view>
</view>
</view>
<view class="blessing-more" @click="handlerMore">
<image v-if="hasWallNext" class="more-icon"
src="xx/blessingGame/arr-down.png">
</image>
<view class="blessing-more-text">{{ blessingStatus }}</view>
</view>

.blessing-con 定义外层容器为flex布局,同时设置主轴对齐方式为space-between


.blessing-con-half定义左右两侧的容器的样式


.blessing-con-half-item定义每一个小盒子的样式


.blessing-con {
padding: 32rpx 20rpx;
display: flex;
justify-content: space-between;
height: 1100rpx;
overflow-y: auto;
.blessing-con-half {
width: 320rpx;
height: 100%;
box-sizing: border-box;
.blessing-con-half-item {
width: 100%;
height: auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
margin: 0 0 24rpx;
position: relative;
}
}
}

这里每个小盒子的背景色按蓝-黄-红的顺序,同时通过伪类给盒子顶部添加锯齿图片,实现锯齿效果


bgColor: ['blueCol', 'yellowCol', 'pinkCol'], //祝福墙背景

// 不同颜色
.blessing-con-half-item {
&.pinkCol {
&::before {
.setbg(320rpx, 16rpx, 'blessingGame/pink-bg.png');
}
.item-con {
background: #FFE7DF;
}
}

&.yellowCol {
&::before {
.setbg(320rpx, 16rpx, 'blessingGame/orange-bg.png');
}
.item-con {
background: #fff0e0;
}
}

&.blueCol {
&::before {
.setbg(320rpx, 16rpx, 'blessingGame/blue-bg.png');
}
.item-con {
background: #e0f7ff;
}
}
}
}

功能实现


在data中定义两个数组存储左右列表的数据


data(){
return{
blessingWordsList: [],// 祝福墙数据
newBlessingWordsList: [],//已添加的数据
newBlessingWordsList1: [],//左列表
newBlessingWordsList2: [],//右列表
isloading:false,//是否正在加载
hasWallNext:false,//是否有下一页
leftHeight: 0,//左高度
rightHeight: 0,//右高度
blessingWordsCount: 0,//计数器
isActive: 0, //tab初始化索引
timer:null,//定时器
}
}

调取接口请求列表数据



  • 第一次请求数据需要初始化列表数据和计数器
  • 每一次请求完都需要开启定时器

// 获取祝福墙列表(type=1则请求下一页)
async getBlessingWall(type = 0) {
try {
let res = await api.blessingWall({
activityId: this.activityId,
pageNum: this.pageWallNum,
pageSize: this.pageWallSize
})
this.isloading = false
if (res.code == 1 && res.rows) {
let list = res.rows
this.blessingWordsList = (type==0 ? list : [...this.blessingWordsList, ...list])
if (this.blessingWordsList.length > 0 && !this.timer && this.isActive == 1) {
if(this.pageWallNum == 1){
this.newBlessingWordsList = []
this.newBlessingWordsList1 = []
this.newBlessingWordsList2 = []
this.blessingWordsCount = 0
}
this.start()
}
// 处理请求下一页的情况
if (type == 1) {
this.start()
}
this.hasWallNext = res.hasNext
if (!this.hasWallNext) {
this.blessingStatus = "没有更多了哟"
} else {
this.blessingStatus = "点击加载更多"
}
}
} catch (error) {
console.log(error)
}
},
// 加载更多
async handlerMore() {
if (this.hasWallNext && !this.isloading) {
this.isloading = true
this.pageWallNum++
await this.getBlessingWall(1)
}
},

开启一个定时器,用于动态添加左右列表的数据


start() {
// 清除定时器
clearInterval(this.timer)
this.timer = null;

this.timer = setInterval(() => {
let len = this.blessingWordsList.length
if (this.blessingWordsCount < len) {
let isHave = false
// 在列表中获取一个元素
let item =this.blessingWordsList[this.blessingWordsCount]
// 判断新列表中是否已经存在相同元素,防止重复添加
this.newBlessingWordsList.forEach((tmp)=>{
if(tmp.id == item.id){
isHave = true
}
})
// 如果不存在
if (!isHave) {
this.newBlessingWordsList.push(item)//添加该元素
this.$nextTick(() => {
this.getHei(item)//添加元素到左右列表
})
}
} else {
// 遍历完列表中的数据,则清除定时器
clearInterval(this.timer)
this.timer = null;
}
}, 10)
}

计算当前左右容器的高度,判断数据要添加到哪一边



  • 使用uni-app的方法获取左右容器的dom对象,再获取他们当前的高度
  • 比较左右高度,向两个数组动态插入数据
  • 每插入一条数据,计数器+1

getHei(item) {
const query = uni.createSelectorQuery().in(this)
// 左边
query.select('#leftHalf').boundingClientRect(res => {
if (res) {
this.leftHeight = res.height
}
// 右边
const query1 = uni.createSelectorQuery().in(this)
query1.select('#rightHalf').boundingClientRect(dataRight => {
if (dataRight) {
this.rightHeight = dataRight.height != 0 ? dataRight.height : 0
if (this.leftHeight == this.rightHeight || this.leftHeight < this.rightHeight) {
// 相等 || 左边小
this.newBlessingWordsList1.push(item)
} else {
// 右边小
this.newBlessingWordsList2.push(item)
}
}
this.blessingWordsCount++
}).exec()
}).exec()
},

这里有一个注意点,调用start方法的时候,必须确保页面渲染了左右容器的元素,否则会拿不到容器的高度


比如我这个项目是有tab切换的!


微信图片_20230731231616.jpg
进入页面的时候会请求一次数据,这时候因为tab初始状态在0,所以并不会调用start方法,要到切换tab到1时,才会调用start方法开始计算高度。


data(){
return{
isActive: 0, //tab初始化索引
timer:null,//定时器
}
}
async onLoad(options) {
this.getBlessingWall()
}
// tab选项卡切换
tabClick(index) {
this.isActive = index
this.isLoaded = false;
if (this.blessingWordsList.length > 0 && !this.timer && this.isActive == 1) {
if(this.pageWallNum == 1){
this.newBlessingWordsList = []
this.newBlessingWordsList1 = []
this.newBlessingWordsList2 = []
this.blessingWordsCount = 0
}
this.start()
}
},

最后


这次选用了flex实现瀑布流,实现瀑布流的方式还有其他几种方法,后续有机会的话,我会补充其他几种方式,如果感兴趣的话,可以点点关注哦!


作者:藤原豆腐店
来源:juejin.cn/post/7260713996165021754

0 个评论

要回复文章请先登录注册