vue+uniapp瀑布流布局多种实现方式示例代码

前言

瀑布流布局是网页设计常见的一种布局,一般用于图片多列展示。列宽固定,图片根据自身高度自适应交错排列。

一、实现原理

通过动态计算哪一列高度最低,就把图片放置该列下显示,直至所有图片分列完毕

计算哪一列高度最低具体实现过程又分2种方式:

方式1:通过计算每一列每张图片渲染后高度进行累加就是该列的高度,记录每列累加高度比较大小

方式2:直接通过图片父级元素高度(列div)来判断哪一列最低

区别:方式1无需等待图片真实渲染完成在比较高度,方式2需要等待图片真实渲染完成在获取高度

二、代码实现

以左右2列为例

<template>
 <div class="page">
 <!-- 左图片列表 -->
 <div class="left" ref="left">
 <img
 class="img"
 v-for="(item, index) in leftList"
 :key="index"
 :src="item"
 />
 </div>
 <!-- 右图片列表 -->
 <div class="right" ref="right">
 <img
 class="img"
 v-for="(item, index) in rightList"
 :key="index"
 :src="item"
 />
 </div>
 </div>
</template>
<style scoped>
.page {
 width: 100%;
 display: flex;
 align-items: flex-start;
 padding: 0 1%;
 box-sizing: border-box;
}
.left,
.right {
 margin: 0 auto;
 width: 48%;
}
.img {
 width: 100%;
 height: auto;
 margin-bottom: 10px;
}
</style>

1.方式1(图片高度累加比较法)

<script>
export default {
 data() {
 return {
 imgList: [
 "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
 "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
 "https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
 "https://img1.baidu.com/it/u=3866290852,3566512524&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500",
 "https://img2.baidu.com/it/u=1114729443,1120710416&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500",
 "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
 "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
 "https://img2.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
 ], //所有图片
 leftList: [], //左边列图片
 rightList: [], //右边列图片
 leftHeight: 0, //左边列高度
 rightHeight: 0, //右边列高度
 columnWidth: 0, //列宽度
 };
 },
 mounted() {
 this.$nextTick(() => {
 this.columnWidth = this.$refs.left.clientWidth;
 this.setWaterFallLayout();
 });
 },
 methods: {
 //方法1
 async setWaterFallLayout() {
 for (let item of this.imgList) {
 let img = new Image();
 img.src = item;
 try{
 let h = await this.getImgHeight(img);//图片渲染后高度
 if (this.leftHeight <= this.rightHeight) {//左边列比右边低,图片放入左边
 this.leftList.push(item);
 this.leftHeight += h;
 } else {//否则,图片放入右边
 this.rightList.push(item);
 this.rightHeight += h;
 }
 }catch(e){
 console.log(e)
 }
 }
 },
 //获取图片高度
 getImgHeight(img) {
 return new Promise((resolve,reject) => {
 //图片加载完成
 img.onload = () => {
 let h = (img.height / img.width) * this.columnWidth;//计算图片渲染后高度
 resolve(h);
 };
 //加载出错
 img.onerror=()=>{
 reject('error')
 }
 });
 },
 },
};
</script>

2.方式2(父元素高度比较法)

每次放入图片需要等待渲染后再重新计算父元素高度,关键代码 await this.$nextTick()

<script>
export default {
 data() {
 return {
 imgList: [
 "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
 "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
 "https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
 "https://img1.baidu.com/it/u=3866290852,3566512524&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500",
 "https://img2.baidu.com/it/u=1114729443,1120710416&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500",
 "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
 "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
 "https://img2.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
 ], //所有图片
 leftList: [], //左边列表图片
 rightList: [], //右边列表图片
 };
 },
 mounted() {
 this.$nextTick(() => {
 this.setWaterFallLayout2();
 });
 },
 methods: {
 //方法2
 async setWaterFallLayout2() {
 for (let item of this.imgList) {
 if (this.$refs.left.clientHeight <= this.$refs.right.clientHeight) {//左边列比右边低,图片放入左边
 this.leftList.push(item);
 } else {//否则图片放入右边
 this.rightList.push(item);
 }
 await this.$nextTick();//等待渲染完成后重新比较左右高度
 }
 },
 },
};
</script>

三.uniapp实现

由于uniapp获取元素高度和vue有所区别,造成实现瀑布流方式也需要调整。我们知道uniapp不能通过this.$ref.xx.clientHeight获取元素高度,而需要通过uni.createSelectorQuery().in(this).select(‘.xxxx’).boundingClientRect().exec()来获取,且经过实测当图片动态加入列后通过该api计算出父元素真实高度是不准确的,所以uniapp瀑布流布局实现方式只能通过方法1(也即图片高度累加法)进行实现,除了上面方法1通过img.onload来获取图片高度外,uniapp还提供uni.getImageInfo方法来更方便获取图片高度。

代码实现

<template>
	<view class="page">
	<view class="left" ref="left">
	<image class="image" v-for="(item,i) in leftList" :key="i" :src="item" mode="widthFix"></image>
	</view>
	<view class="right" ref="right">
	<image class="image" v-for="(item,i) in rightList" :key="i" :src="item" mode="widthFix"></image>
	</view>
	</view>
</template>
<style lang="scss">
	.page {
	width: 100%;
	display: flex;
	align-items: flex-start;
	padding: 0 1%;
	box-sizing: border-box;
	}

	.left,
	.right {
	margin: 0 auto;
	width: 48%;
	}

	.image {
	width: 100%;
	height: auto;
	margin-bottom: 10px;
	}
</style>
<script>
	export default {
	data() {
	return {
	imageList: [
	"https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
	"https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
	"https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
	"https://img1.baidu.com/it/u=3866290852,3566512524&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500",
	"https://img2.baidu.com/it/u=1114729443,1120710416&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500",
	"https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
	"https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
	"https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
	], //所有图片
	leftList: [], //左边列图片
	rightList: [], //右边列图片
	leftHeight: 0, //左边列高度
	rightHeight: 0, //右边列高度
	columnWidth: 0 //列宽度
	}
	},
	mounted() {
	this.$nextTick(() => {
	uni.createSelectorQuery().in(this).select('.left').boundingClientRect(res => {
	this.columnWidth = res.width
	//方法1
	this.setWaterFallLayout()
	//方法2
	// this.setWaterFallLayout2()
	}).exec()
	})
	},
	methods: {
	//方法1通过img.onload
	async setWaterFallLayout() {
	for (let item of this.imageList) {
	let img = new Image()
	img.src = item
	try {
	let h = await this.getImgHeight(img)
	if (this.leftHeight <= this.rightHeight) {
	this.leftList.push(item)
	this.leftHeight += h
	} else {
	this.rightList.push(item)
	this.rightHeight += h
	}
	} catch (e) {
	console.log(e)
	}

	}

	},
	//获取图片高度
	getImgHeight(img) {
	return new Promise((resolve, reject) => {
	img.onload = () => {
	let h = img.height / img.width * this.columnWidth
	resolve(h)
	}
	//加载出错
	img.onerror = () => {
	reject('error')
	}
	})
	},
	//方法2通过uni.getImageInfo
	async setWaterFallLayout2() {
	for (let item of this.imageList) {
	uni.getImageInfo({
	src: item,
	success: e => {
	if (this.leftHeight <= this.rightHeight) {
	this.leftList.push(item)
	this.leftHeight += e.height
	} else {
	this.rightList.push(item)
	this.rightHeight += e.height
	}

	}
	})
	}
	}

	},

	}
</script>

四、多列实现

多列实现和2列一样,动态生成每列图片数据和记录每列高度

代码实现

以最简单的父元素高度比较法(方式2)为例实现,图片高度累加比较法(方式1)自行类比实现

<template>
 <div class="page">
 <div
 class="column"
 ref="column"
 v-for="(item, index) in columnList"
 :key="index"
 >
 <img class="img" v-for="(n, i) in item" :key="i" :src="n" />
 </div>
 </div>
</template>
<style scoped>
.page {
 width: 100%;
 display: flex;
 align-items: flex-start;
 padding: 0 1%;
 box-sizing: border-box;
}

.column {
 flex: 1;
 padding: 0 10px;
 box-sizing: border-box;
 width: 0;
}

.img {
 width: 100%;
 height: auto;
 margin-bottom: 10px;
}
</style>
<script>
export default {
 data() {
 return {
 imgList: [
 "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
	"https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
	"https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
	"https://img1.baidu.com/it/u=3866290852,3566512524&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500",
	"https://img2.baidu.com/it/u=1114729443,1120710416&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500",
	"https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
	"https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
	"https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
	"https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
	"https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
 ], //所有图片
 columnList: [], //分配后的每列图片
 columWidth: 0, //每列宽度
 columnCount: 5, //显示几列
 };
 },
 created() {
 //初始化数据
 for (let i = 0; i < this.columnCount; i++) {
 this.columnList.push([]);//生成每列图片数组
 }
 },
 mounted() {
 this.$nextTick(()=>{
 this.setWaterFallLayout();
 })
 },
 methods: {
 //瀑布布局
 async setWaterFallLayout() {
 for (let item of this.imgList) {
 let columnHeight = this.$refs.column.map((item) => item.clientHeight); //每列高度数组
 let min = Math.min(...columnHeight); //找出最小高度值
 let index = columnHeight.findIndex((item) => item === min); //找出最小高度列的索引
 this.columnList[index].push(item);//放入图片
 await this.$nextTick(); //等待渲染完成后重新比较高度
 }
 },
 },
};
</script>

总结

作者:pixle0原文地址:https://blog.csdn.net/sd1sd2/article/details/128599997

%s 个评论

要回复文章请先登录注册