微信小程序实现瀑布流布局
实现的效果图如下显示:
一,需求分析
- 布局分为两列,两列的高度随着图片的插入而改变
- 默认给左右两列插入一张图片,插入图片后,根据两列的的高度做为判断依据,依次给高度更低的一列插入图片
二,使用Component实现瀑布流,复用性更强。
通过Component把瀑布流封装成一个组件,可以再其他需要瀑布流的页面调用,减少了代码量,规范代码,更方便,效率更高。
1,首先自定义瀑布流组件
在需要瀑布流页面的json文件引入组件
使用瀑布流组件
2,设置组件wxml布局
<view class="waterfall clear">
<view class="leftfall left" id="leftfall">
<view class="img-item" wx:for="{{ leftfall }}" wx:key="this">
<image src="{{ item.photo_url }}" lazy-load="{{ true }}" mode="widthFix" bindload="waterfall"></image>
</view>
<text class='recom'>
珍藏已久的vsco调色合集,最美素颜。
</text>
<view class="info clear">
<image class='handimg' mode="widthFix"></image>
<text class='dollar'>¥ 233.5</text>
</view>
</view>
<view class="rightfall right" id="rightfall">
<view class="img-item" wx:for="{{ rightfall }}" wx:key="this">
<image src="{{ item.photo_url }}" lazy-load="{{ true }}" mode="widthFix" bindload="waterfall"></image>
</view>
<text class='recom'>
珍藏已久的vsco调色合集,最美素颜。
</text>
<view class="info clear">
<image class='handimg' mode="widthFix"></image>
<text class='dollar'>¥ 233.5</text>
</view>
</view>
</view>
image组件的mode设置为widthFix为了让图片适应容器的宽度并且等比例缩放
3,设置wxss样式如下
page{
background: #f5f5f5;
}
.maincont{
width: 100%;
background: #ffffff;
}
.waterfall{
padding:30rpx;
background: #f5f5f5;
}
.leftfall,.rightfall,.imgbox{
width: 340rpx;
height: auto;
}
.img-item{
margin: 0 0 30rpx 0;
border-radius: 15rpx;
overflow: hidden;
box-shadow: 2rpx 2rpx 10rpx #c0c0c0;
}
.recom{
width: 90%;
height: 90rpx;
margin: 0 5% 0 5%;
line-height: 30rpx;
font-size: 28rpx;
float: left;
font-weight: bold;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
.handimg{
width: 50rpx;
height: 50rpx;
float: right;
display: inline-block;
border-radius: 50%;
background: skyblue;
}
.dollar{
height: 50rpx;
line-height: 50rpx;
margin: 0 10rpx 0 0;
display: inline-block;
float: left;
font-size: 24rpx;
font-weight: bold;
color: red;
}
.info{
width: 90%;
height: 50rpx;
margin:25rpx 5% 15rpx 5%;
float: left;
}
.clear::after{
content: '';
clear: both;
height: 0;
display: block;
visibility: hidden;
}
根据以上代码,可以看出在主页面获取的数据dataFall通过组件传值的方式,传送到子组件,然后再将数据放进leftfall和rightfall这两个数组里面
具体的参数说明
参数说明:
dataFall为从父组件获取到的瀑布流数据,当dataFall的数据出现改变时候,在observer:function(){}的异步操作中把数据赋值给imglist
properties: {
dataFall: {
type: Array,
// value: [],
observer: function (newVal, oldVal) {
this.setData({
imgList: newVal
})
if (this.data.imgList.length !== 0) {
setTimeout(function () {
// 数据改变以后首次执行瀑布流代码
that.waterfall()
// 更改加载瀑布流的状态
that.setData({
fallStatus: false
})
}, 0)
}
}
},
},
/**
* 组件的初始数据
*/
data: {
imgList: [], // 从父组件获取到的瀑布流总数据
fallStatus: false, // 瀑布流加载的状态
leftfall: [], // 瀑布流左列容器的数据组
rightfall: [], // 瀑布流右列容器的数据组
},
具体的逻辑实现
一开始我们定义两个变量来记录leftfall和rightfall这两列装载图片容器的高度,通过小程序内置Api接口wx.createSelectorQuery()来获取两列容器的高度,一开始两列容器的高度都为0的时候,默认给左边第一列容器插入一张图片,后续进行对比当leftfall的高度大于rightfall的高度时候将数据插入rightfall 数组 反之将数据插入leftfall数组。
具体实现步骤
1,获取两列容器的高度
自定义变量leftfall和rightfall,通过微信内置Api接口wx.createSelectorQuery().selectAll(’#leftfall,#rightfall’).boundingClientRect(function (res) {}).exec();来获取两列容器的高度。
需要注意的地方:在自定义组件内wx.createSelectorQuery()存在一个指向性问题所以在后面需要加上in(this)重新指向自定义组件才可以获取组件内的元素的高度属性。
let left_hei = ''
let right_hei = ''
let that = this
const query = wx.createSelectorQuery().in(this);
query.selectAll('#leftfall,#rightfall').boundingClientRect(function (res) {
left_hei = res[0].height // 获取左列容器的高度
right_hei = res[1].height // 获取右列容器的高度
}).exec();
2,对比两列容器的高度判断数据插入那个数组
(1)为了防止有些图片在同步加载的过程中图片过大加载过慢而导致瀑布流图片墙分布的不规则,所以我们把判断两列高度的代码先封装成一个inserImg方法,
function inserImg() {
if (that.data.imgList.length != 0) {
if (left_hei === right_hei) {
that.data.leftfall.push(that.data.imgList[0])
that.setData({
leftfall: that.data.leftfall
})
} else if (left_hei > right_hei) {
that.data.rightfall.push(that.data.imgList[0])
that.setData({
rightfall: that.data.rightfall
})
} else if (left_hei < right_hei) {
that.data.leftfall.push(that.data.imgList[0])
that.setData({
leftfall: that.data.leftfall
})
}
// 插入数据后,从总数据里删除已插入的数据
that.setData({
imgList: that.data.imgList.slice(1)
})
}
}
(2)在执行完wx.createSelectorQuery()获取到leftfall和rightfall后的异步操作里面再去执行inserImg方法去做两列容器的高度判断,在把数据插入到那一列中。
let left_hei = ''
let right_hei = ''
let that = this
const query = wx.createSelectorQuery().in(this);
query.selectAll('#leftfall,#rightfall').boundingClientRect(function (res) {
left_hei = res[0].height // 获取左列容器的高度
right_hei = res[1].height // 获取右列容器的高度
// 获取完两列容器的高度后判断数据插入那一列数组
inserImg();
}).exec();
3,分配所有数据
因为每一次任意一列插入一张图片的时候都需要重新获取leftfall和rightfall这两容器的高度作比较,所以我们把全部的瀑布流代码封装一个waterfall的方法,然后使用image组件里的bindload方法在插入一张图片等图片加载完后自动执行一次waterfall方法,直到把imglist里的所有数据都分配到leftfall和rightfall。
image组件代码
<image src="{{ item.photo_url }}" lazy-load="{{ true }}" mode="widthFix" bindload="waterfall"></image>
waterfall(event) {
let left_hei = ''
let right_hei = ''
let that = this
const query = wx.createSelectorQuery().in(this);
query.selectAll('#leftfall,#rightfall').boundingClientRect(function (res) {
left_hei = res[0].height // 获取左列容器的高度
right_hei = res[1].height // 获取右列容器的高度
// 获取左右两列容器高度后对比两边容器高度插入数据
inserImg();
}).exec();
function inserImg() {
if (that.data.imgList.length != 0) {
if (left_hei === right_hei) {
that.data.leftfall.push(that.data.imgList[0])
that.setData({
leftfall: that.data.leftfall
})
} else if (left_hei > right_hei) {
that.data.rightfall.push(that.data.imgList[0])
that.setData({
rightfall: that.data.rightfall
})
} else if (left_hei < right_hei) {
that.data.leftfall.push(that.data.imgList[0])
that.setData({
leftfall: that.data.leftfall
})
}
// 插入数据后,从总数据里删除已插入的数据
that.setData({
imgList: that.data.imgList.slice(1)
})
} else {
// 瀑布流加载结束向父组件传递信息,可进行上拉加载操作,获取分页数据
that.triggerEvent('waterFallResh', { loading: true })
that.setData({
fallStatus: true
})
}
}
}
整合完整的瀑布流js代码
具体代码如下:
Component({
/**
* 组件的属性列表
*/
options: {
addGlobalClass: true
},
properties: {
dataFall: {
type: Array,
// value: [],
observer: function (newVal, oldVal) {
this.setData({
imgList: newVal
})
if (this.data.imgList.length !== 0) {
setTimeout(function () {
// 数据改变以后首次执行瀑布流代码
that.waterfall()
// 更改加载瀑布流的状态
that.setData({
fallStatus: false
})
}, 0)
}
}
},
},
/**
* 组件的初始数据
*/
data: {
imgList: [], // 从父组件获取到的瀑布流总数据
fallStatus: false, // 瀑布流加载的状态
leftfall: [], // 瀑布流左列容器的数据组
rightfall: [], // 瀑布流右列容器的数据组
},
methods: {
// 瀑布流代码
waterfall(event) {
let left_hei = ''
let right_hei = ''
let that = this
const query = wx.createSelectorQuery().in(this);
query.selectAll('#leftfall,#rightfall').boundingClientRect(function (res) {
left_hei = res[0].height // 获取左列容器的高度
right_hei = res[1].height // 获取右列容器的高度
// 获取左右两列容器高度后对比两边容器高度插入数据
inserImg();
}).exec();
function inserImg() {
if (that.data.imgList.length != 0) {
if (left_hei === right_hei) {
that.data.leftfall.push(that.data.imgList[0])
that.setData({
leftfall: that.data.leftfall
})
} else if (left_hei > right_hei) {
that.data.rightfall.push(that.data.imgList[0])
that.setData({
rightfall: that.data.rightfall
})
} else if (left_hei < right_hei) {
that.data.leftfall.push(that.data.imgList[0])
that.setData({
leftfall: that.data.leftfall
})
}
// 插入数据后,从总数据里删除已插入的数据
that.setData({
imgList: that.data.imgList.slice(1)
})
} else {
// 瀑布流加载结束向父组件传递信息,可进行上拉加载操作,获取分页数据
that.triggerEvent('waterFallResh', { loading: true })
that.setData({
fallStatus: true
})
}
}
},
}
})