目录
1、什么是瀑布流?
瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。
2、核心实现
我们主要通过两个数组进行实现,即分为左右数组。由于每一个图片的高度都是不一样的,需要我们实时计算图片的高度,并判断图片应该添加在哪个数组中。
3、实现过程
一、在components目录下创建名为water-fall的组件,water-fall.wxml主要代码如下:
<!--components/water-fall/water-fall.wxml-->
<view class="wrapper">
<view class="waterfall" id="list">
<view id="left-column" class="column">
<view wx:for="{{leftList}}" wx:key="index" class="item">
<image src="{{item}}" mode="widthFix" class="pic" wx:if="{{defaultSlot}}" data-src="{{item}}" bindtap="previewImg"/>
<block wx:else>
<!-- 自定义内容 -->
</block>
</view>
</view>
<view id="right-column" class="column">
<view wx:for="{{rightList}}" wx:key="index" class="item">
<image src="{{item}}" mode="widthFix" class="pic" wx:if="{{defaultSlot}}" data-src="{{item}}" bindtap="previewImg"/>
<block wx:else>
<!-- 自定义内容 -->
</block>
</view>
</view>
</view>
</view>
water-fall.scss主要代码如下:
/* components/water-fall/water-fall.wxss */
.wrapper {
padding: 15rpx 0;
.waterfall {
height: 100%;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
.column {
display: flex;
flex-direction: column;
flex: 1;
height: auto;
word-break: break-all;
.item {
width: 98%;
margin-bottom: 10rpx;
.pic {
width: 100%;
border-radius: 20rpx;
box-shadow: 0 3rpx 4rpx rgba(0, 0, 0, 0.2);
}
}
}
}
}
将左右两列数组放置于waterfall 容器中,通过flex布局将两列数组设置为左右两端对齐,并使每列数组column铺满盒子一半。
二、在properties中,设置value接收父组件传入的数据,并在data中分别定义leftList、rightList、tempList、oldTempList四个数组,通过splitData方法对数组进行处理,将每一项分别添加到左leftList、右rightList两列数组中。
在lifetimes
字段内进行声明attached生命周期函数,并复制传入的数据源value给暂存数组tempList。通过observers监听value数据的变化,其代码如下:
组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发。
其中,最重要的生命周期是
created
attached
detached
,包含一个组件实例生命流程的最主要时间点。
- 组件实例刚刚被创建好时,
created
生命周期被触发。此时,组件数据this.data
就是在Component
构造器中定义的数据data
。 此时还不能调用setData
。 通常情况下,这个生命周期只应该用于给组件this
添加一些自定义属性字段。- 在组件完全初始化完毕、进入页面节点树后,
attached
生命周期被触发。此时,this.data
已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。- 在组件离开页面节点树后,
detached
生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则detached
会被触发生命周期方法可以直接定义在
Component
构造器的第一级参数中。自小程序基础库版本 2.2.3 起,组件的的生命周期也可以在
lifetimes
字段内进行声明(这是推荐的方式,其优先级最高)。
Component({
options: {
multipleSlots: true
},
properties: {
value: {
type: Array,
value: []
},
defaultSlot: {
type: Boolean,
value: true
}
},
data: {
leftList: [],
rightList: [],
tempList: [],
oldTempList: [],
},
lifetimes: {
attached() {
this.data.tempList = this.cloneData(this.data.value);
this.splitData();
},
detached() {
// 销毁前的清理操作
}
},
observers: {
'value': function (nVal) {
this.setData({
oldTempList: this.cloneData(nVal),
tempList: this.findDifferentItems(nVal, this.data.oldTempList)
});
this.splitData();
}
},
methods: {
async splitData() {
if (!this.data.tempList.length) return;
let leftRect = await this.getRect('#left-column');
let rightRect = await this.getRect('#right-column');
let item = this.data.tempList[0];
if (!item) return;
if (leftRect.height <= rightRect.height) {
let arr = this.data.leftList.concat(item)
this.setData({
leftList: arr
});
} else {
let arr = this.data.rightList.concat(item)
this.setData({
rightList: arr
});
}
this.data.tempList.splice(0, 1);
if (this.data.tempList.length) {
this.splitData();
return;
}
},
getRect(selector) {
return new Promise((resolve, reject) => {
const query = wx.createSelectorQuery().in(this);
query.select(selector).boundingClientRect();
query.exec(function (res) {
if (res && res[0]) {
resolve(res[0]);
} else {
reject('获取节点信息失败');
}
});
});
},
cloneData(data) {
return JSON.parse(JSON.stringify(data));
},
clear() {
this.setData({
leftList: [],
rightList: []
});
},
/* 图片预览 */
previewImg(e) {
let currentUrl = e.currentTarget.dataset.src
let imgs = [...this.data.value]
wx.previewImage({
current: currentUrl,
urls: imgs
})
},
/* 数组过滤 */
findDifferentItems(array1, array2) {
const uniqueArray = [];
// 遍历第一个数组
for (let i = 0; i < array1.length; i++) {
let found = false;
// 检查第二个数组中是否存在相同的项
for (let j = 0; j < array2.length; j++) {
if (array1[i].id === array2[j].id) {
found = true;
break;
}
}
// 如果第二个数组中不存在相同的项,则将其添加到结果数组中
if (!found) {
uniqueArray.push(array1[i]);
}
}
return uniqueArray;
}
}
});
4、应用
一、 page页面中,先在.josn文件中引入water-fall组件:
{
"usingComponents": {
"water-fall": "/components/water-fall/water-fall"
},
"navigationBarTitleText": "相册"
}
二、直接在.wxml引入water-fall组件:
<water-fall value="{{PhotoList}}"></water-fall>