likes
comments
collection
share

uniapp实现瀑布流布局

作者站长头像
站长
· 阅读数 16

前言

看到很多关于瀑布流写法问题,有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的排列布局

uniapp实现瀑布流布局 下面是我们期望的瀑布流排列布局方式

uniapp实现瀑布流布局 不多说直接上代码

<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实现瀑布流布局 如果不是uniapp需要自己改造一下,也可以通过后端返回的接口数据进行渲染,将原来的数据包替换,但是替换的时候要注意一下是图片加载计算列高,我们需要在数据做一下变化调整,如果您是3列column的键值修改为您自己想要的列数就可以了

大概逻辑就是获取每列高度,每次图片加载时把该图片加载至列高度最短的那一列进行实现,我们一般通过请求的数据和懒加载这个需要大概改造一下,将list的数据变为后端给到的数据包,需要注意的是这个是通过图片初始化,所以服务器端的数据必须含有图片。 今天的内容就写到这里,欢迎大家一起讨论,提升前端技术!!! 如有问题,欢迎在评论区指出互相讨论 如果这篇文章对各位大佬有帮助,请动动你们的小手,辛苦点点赞评论,给一点动力,谢谢各位大佬!!!

转载自:https://juejin.cn/post/7231050577366859831
评论
请登录