uniapp实现瀑布流布局
前言
看到很多关于瀑布流写法问题,有flex布局,column布局,等等方面,但是自己使用起来有一些问题,所以现在自己写一个关于瀑布流的文章,新手写法,写的不好勿喷,先介绍一下flex和column写法
flex布局原理
先来一个包住整个瀑布流的盒子,如果是2列,给该盒子设置display:flex;flex-wrap:wrap;完成这一步我们就可以有一个flex布局,超出该盒子宽度自动换行,然后在这个大盒子内部设置2个div容器,一列取奇数,一列取偶数进行实现,
<view class="box" style=‘’>
<view v-for="(item,idx) in dataList" :key="idx">
<view v-if="idx%2==1"> {{item.img}} </view>
</view>
<view v-for="(item,idx) in dataList" :key="idx">
<view v-if="idx%2!=1"> {{item.img}} </view>
</view>
</view>
但是这样有个弊端,排列的顺序是按照奇数偶数来控制的,可能跟自己的有偏差,我们采用column布局也是一样的道理无法控制排列顺序,column布局百度有很多,这里就不写了,而我们一般的瀑布流是多列情况哪一列最短,那么下一个元素补位在最短那一列, 这是用column的排列布局
下面是我们期望的瀑布流排列布局方式
不多说直接上代码
<template>
<view class="padding-top-lg">
<view class="waterfalls-flow">
<view v-for="(item,index) in data.column" :key="index" class="waterfalls-flow-column" :style="{'width':w,'margin-left':index==0?0:m}" :id="`waterfalls_flow_column_${index+1}`">
<view class="column-value" v-for="(item2,index2) in data[`column_${index+1}`]" :key="index2">
<image :src="item2.image" mode="widthFix" @load="imgLoad(item2)" @error="imgError(item2)" class="imgsty"></image>
<text>{{item2.title}}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
reactive,
watch,
computed,
getCurrentInstance,
onMounted
} from 'vue';
const _this = getCurrentInstance();
const data = reactive({
list: [],
column: 2,
columnSpace: 1,
});
// 数据赋值
data.list = [{
image: 'https://via.placeholder.com/200x500.png/ff0000',
title: '我是标题1',
desc: '描述描述描述描述描述描述描述描述1'
},
{
image: 'https://via.placeholder.com/200x200.png/2878ff',
title: '我是标题2',
desc: '描述描述描述描述描述描述描述描述2'
},
{
image: 'https://via.placeholder.com/200x100.png/FFB6C1',
title: '我是标题3',
desc: '描述描述描述描述描述描述描述描述3'
},
{
image: 'https://via.placeholder.com/200x300.png/9400D3',
title: '我是标题4',
desc: '描述描述描述描述描述描述描述描述4'
},
{
image: 'https://via.placeholder.com/100x240.png/B0E0E6',
title: '我是标题5',
desc: '描述描述描述描述描述描述描述描述5'
},
{
image: 'https://via.placeholder.com/140x280.png/7FFFAA',
title: '我是标题6',
desc: '描述描述描述描述描述描述描述描述6'
},
{
image: 'https://via.placeholder.com/40x60.png/EEE8AA',
title: '我是标题7',
desc: '描述描述描述描述描述描述描述描述7'
},
{
image: 'https://via.placeholder.com/200x500.png/ff0000',
title: '我是标题1',
desc: '描述描述描述描述描述描述描述描述1'
},
{
image: 'https://via.placeholder.com/200x200.png/2878ff',
title: '我是标题2',
desc: '描述描述描述描述描述描述描述描述2'
},
{
image: 'https://via.placeholder.com/200x100.png/FFB6C1',
title: '我是标题3',
desc: '描述描述描述描述描述描述描述描述3'
},
{
image: 'https://via.placeholder.com/200x300.png/9400D3',
title: '我是标题4',
desc: '描述描述描述描述描述描述描述描述4'
},
{
image: 'https://via.placeholder.com/100x240.png/B0E0E6',
title: '我是标题5',
desc: '描述描述描述描述描述描述描述描述5'
},
{
image: 'https://via.placeholder.com/140x280.png/7FFFAA',
title: '我是标题6',
desc: '描述描述描述描述描述描述描述描述6'
},
{
image: 'https://via.placeholder.com/40x60.png/EEE8AA',
title: '我是标题7',
desc: '描述描述描述描述描述描述描述描述7'
},
];
// 计算列宽
const w = computed(() => {
const column_rate = `${100 / data.column - (+data.columnSpace)}%`;
return column_rate;
})
// 计算margin
const m = computed(() => {
const column_margin = `${(100-(100 / data.column - (+data.columnSpace)).toFixed(5)*data.column)/(data.column-1)}%`;
return column_margin;
})
// 每列的数据初始化
for (let i = 1; i <= data.column; i++) {
data[`column_${i}`] = [];
}
// 获取最小值的对象
const getMin = (a, s) => {
let m = a[0][s];
let mo = a[0];
for (var i = a.length - 1; i >= 0; i--) {
if (a[i][s] < m) {
m = a[i][s];
}
}
mo = a.filter(i => i[s] == m);
return mo[0];
}
// 计算每列的高度
function getMinColumnHeight() {
return new Promise(resolve => {
const heightArr = [];
for (let i = 1; i <= data.column; i++) {
const query = uni.createSelectorQuery().in(_this);
query.select(`#waterfalls_flow_column_${i}`).boundingClientRect(data => {
heightArr.push({
column: i,
height: data.height
});
}).exec(() => {
if (data.column <= heightArr.length) {
resolve(getMin(heightArr, 'height'));
}
});
}
})
};
async function initValue(i) {
if (i >= data.list.length) return false;
const minHeightRes = await getMinColumnHeight();
data[`column_${minHeightRes.column}`].push({
...data.list[i],
index: i
});
}
onMounted(() => {
initValue(0);
})
// 图片加载完成
function imgLoad(item) {
const i = item.index;
initValue(i + 1);
}
// 图片加载失败
function imgError(item) {
const i = item.index;
initValue(i + 1);
}
</script>
<style scoped lang="scss">
.waterfalls-flow {
padding-top: 50upx;
&-column {
float: left;
padding: 0 0 200upx;
}
}
.column-value {
width: 100%;
}
.imgsty {
width: 100%
}
</style>
这是效果图
如果不是uniapp需要自己改造一下,也可以通过后端返回的接口数据进行渲染,将原来的数据包替换,但是替换的时候要注意一下是图片加载计算列高,我们需要在数据做一下变化调整,如果您是3列column的键值修改为您自己想要的列数就可以了
大概逻辑就是获取每列高度,每次图片加载时把该图片加载至列高度最短的那一列进行实现,我们一般通过请求的数据和懒加载这个需要大概改造一下,将list的数据变为后端给到的数据包,需要注意的是这个是通过图片初始化,所以服务器端的数据必须含有图片。 今天的内容就写到这里,欢迎大家一起讨论,提升前端技术!!! 如有问题,欢迎在评论区指出互相讨论 如果这篇文章对各位大佬有帮助,请动动你们的小手,辛苦点点赞评论,给一点动力,谢谢各位大佬!!!
转载自:https://juejin.cn/post/7231050577366859831