我是靠谱客的博主 贤惠大叔,这篇文章主要介绍手把手写一个uniapp小程序瀑布流组件子组件父组件,现在分享给大家,希望可以做个参考。

手把手写一个uniapp小程序瀑布流组件

  • 子组件
  • 父组件

我的实现思路是 将列表拆分成左右两列的数组,然后在追加数据时,获取两个的高度,哪个矮一点我就加到哪个数组里。组件看起来样式比较丑,这是完全自定义的,想要多好看就看你修饰了。

已测试支持的平台

  • 微信小程序
  • 支付宝小程序
  • QQ小程序
  • 百度小程序
  • 飞书小程序
  • H5
  • 安卓APP

暂不支持的平台

  • 抖音/头条小程序
  • 快手小程序
    原因:uniapp编译产出代码错误导致,可自己修改源码解决,或等官方修复:https://ask.dcloud.net.cn/question/166430

不支持的平台

  • 快应用(编译不通过,兼容性太差,直接不考虑了)

如果组件不显示,调一下组件clear或update方法即可。如果没列出来的说明没测试到的,不代表不支持

在这里插入图片描述
在这里插入图片描述

子组件

1.首先创建一个waterfall-flow.vue的组件, 然后在template中写入以下代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
<template> <view class="flex" :style="{'padding': '0 '+ (offset || offsetO)}"> <view class="left" id="left" :style="{'margin-right': offset || offsetW}"> <slot name="left" v-bind:leftList="leftList"></slot> </view> <view class="right" id="right"> <slot name="right" v-bind:rightList="rightList"></slot> </view> </view> </template>

<slot> 我们用来放置瀑布流的主体内容,name=“left” 是我定义的作用域插槽的名字,v-bind:leftList="leftList"就是绑定子组件的参数,以供父组件能够直接使用它。

2.样式部分

复制代码
1
2
3
4
5
6
7
8
9
10
11
.flex{ display: flex; /* 使用flex布局 */ width: 100%; /* 宽度占满父组件的宽就好,不设定具体宽 */ height: auto; /* 高度设为由内容自动撑开 */ box-sizing: border-box; /* 这个是必须的,因为我们接下来会接收offset参数来定边距 */ } .left, .right{ flex: 1; /* 自动分配宽度 */ height: max-content; /* 高度需设为内容的最大高度 */ }

3.props我们接收几个参数

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
props: { list: { type: Array, // 列表数据 default: [] }, offset: { type: String, // offset 是offsetW和offsetO的简写,也就是同时包含最外边的边距和元素之间的间距 default: '' // 传有这个属性不为空时offsetW和offsetO将不生效 }, offsetW: { type: String, // offsetW => offset Within 外边距,两个元素的中间边距 default: '10rpx' }, offsetO: { type: String, // offsetO => offset Outside 外边距,两个元素的最外边边距 default: '' }, },

4.data定义左右瀑布流的数组,以及当前队列执行到第几项

复制代码
1
2
3
4
5
6
7
8
data(){ return { leftList: [], // 左边的数据 rightList: [], // 右边的数据 current: 0, // 当前队列执行到第几项了,用于取出list数据,然后分配到对应的leftList还是rightList中 } },

5.写一个监听器,监听list的变化,然后执行队列

复制代码
1
2
3
4
5
6
7
8
9
watch: { list(val){ // 数据追加,如果只是改变数据重新渲染,length长度一样,那么在这之前应该先调clear方法 if(val.length > (this.leftList.length + this.rightList.length)){ this.pushQueue(); } } },

6.现在我们来实现这个队列

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pushQueue(){ if((this.leftList.length + this.rightList.length) >= this.list.length){ // 渲染完毕,向外触发load事件 this.$emit('load', { total: this.current, leftList: this.leftList, rightList: this.rightList }); this.current--; return; }; // 这里得要加个定时器,要不然有时候获取query信息是无的 setTimeout(() => { let query = uni.createSelectorQuery().in(this); query.select('#left').boundingClientRect(); query.select('#right').boundingClientRect(); query.exec((res) => { let leftHeight = res[0].height; // 获取左边列表的高度 let rightHeight = res[1].height; // 获取右边列表的高度 leftHeight <= rightHeight ? this.leftList.push(this.list[this.current]) : this.rightList.push(this.list[this.current]); this.current++; this.pushQueue(); // 执行下一个队列 }); }, 300); },

就是这么简单,大工告成了!下面是瀑布流组件全部的代码,并且添加clearupdate方法,用于数据清除

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<template> <view class="flex" :style="{'padding': '0 '+ (offset || offsetO)}"> <view class="left" id="left" :style="{'margin-right': offset || offsetW}"> <slot name="left" v-bind:leftList="leftList"></slot> </view> <view class="right" id="right"> <slot name="right" v-bind:rightList="rightList"></slot> </view> </view> </template> <script> export default { name: 'waterfall-flow', props: { list: { type: Array, // 列表数据 default: [] }, offset: { type: String, // offset 是offsetW和offsetO的简写,也就是同时包含最外边的边距和元素之间的间距 default: '' // 传有这个属性不为空时offsetW和offsetO将不生效 }, offsetW: { type: String, // offsetW => offset Within 外边距,两个元素的中间边距 default: '10rpx' }, offsetO: { type: String, // offsetO => offset Outside 外边距,两个元素的最外边边距 default: '' }, }, data(){ return { leftList: [], rightList: [], current: 0, } }, watch: { list(val){ // 数据追加,如果只是改变数据重新渲染,length长度一样,那么在这之前应该先调clear方法 if(val.length > (this.leftList.length + this.rightList.length)){ this.pushQueue(); } } }, methods: { // 渲染队列 pushQueue(){ if((this.leftList.length + this.rightList.length) >= this.list.length){ this.$emit('load', { total: this.current, leftList: this.leftList, rightList: this.rightList }); this.current--; return; }; setTimeout(() => { let query = uni.createSelectorQuery().in(this); query.select('#left').boundingClientRect(); query.select('#right').boundingClientRect(); query.exec((res) => { let leftHeight = res[0].height; // 获取左边列表的高度 let rightHeight = res[1].height; // 获取右边列表的高度 leftHeight <= rightHeight ? this.leftList.push(this.list[this.current]) : this.rightList.push(this.list[this.current]); this.current++; this.pushQueue(); // 执行下一个队列 }); }, 300); }, // 清除数据,用于重新渲染 clear(){ this.leftList = []; this.rightList = []; this.current = 0; }, // 额外导出的方法,用于更新数据或强制重新渲染 update(){ this.clear(); this.$nextTick(() => { this.pushQueue(); }) } } } </script> <style scoped> .flex{ display: flex; width: 100%; height: auto; box-sizing: border-box; } .left, .right{ flex: 1; height: max-content; } </style>

父组件

我参考了 uView 1.x 版本的瀑布流组件的写法,所以看起来是差不多的。我假设你已经会引入组件了,那么现在来写dom

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<waterfall-flow :list="list" offset="10rpx" ref="myWaterfallFlow" @load="waterfallFlowLoad"> <template v-slot:left="{leftList}"> <view class="demo-warter" v-for="(item, index) in leftList" :key="index" @click="clickItem(item)"> <!-- 这里的image标签必须添加mode="widthFix"属性,因为我们的高度应该是自适应的 --> <image :src="item.url" mode="widthFix" :lazy-load="true"></image> <view>{{ item.title }}</view> </view> </template> <template v-slot:right="{rightList}"> <view class="demo-warter" v-for="(item, index) in rightList" :key="index" @click="clickItem(item)"> <image :src="item.url" mode="widthFix" :lazy-load="true"></image> <view>{{ item.title }}</view> </view> </template> </waterfall-flow>
  • 来看看css部分,这里很关键!!!
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.demo-warter{ width: 100%; // 此为必须,让其容器自适应组件动态分配的宽度 height: auto; // 除了不能设置固定值,其他皆可,通常设为auto就好了 background-color: #ffaaff; // 盒子的背景色... border-radius: 10rpx; // 盒子圆角... overflow: hidden; // 用于显示圆角效果的... // 更多样式优化自行添加... } // nth-child(n+2) 这种写法快手小程序不支持,可以自己在template中用js判断写style .demo-warter:nth-child(n+2){ margin-top: 10rpx; // 元素的上边距,从第二个元素开始出现10rpx,不设置它们将会挨在一起 } .demo-warter image{ width: 100%; // 此为必须,必须将图片的宽度设为100%,好让它自适应父容器显示 }

ok, 一个瀑布流组件的基本使用已经可以了,接下来是需要注意的地方(父组件)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 下拉刷新 onPullDownRefresh(){ this.list = []; /** * 数据被置空时,this.$refs.myWaterfallFlow.clear()方法是必须要调的 * 如果是强制渲染的场景请使用this.$refs.myWaterfallFlow.update(), 例如排序 * */ this.$refs.myWaterfallFlow.clear(); // 需要注意的地方就是这里,如果列表被置为空,例如下拉刷新时,请调用内部方法清除一下瀑布流的列表 setTimeout(() => { this.getList(); uni.stopPullDownRefresh(); }, 500); }

代码我已放入仓库,点击此处来访问~
我在仓库里用两种方式实现了瀑布流,这个例子讲的是waterfall-flow2的组件
如有写的不对之处,还请大佬不吝赐教,非常感谢!本人目前还在学习当中,不喜勿喷
如有不明确之处还请留言,或添加我的微信:pipiping_


23年3月17日更新分割线

  • 之前版本有bug的已在正文中修改,现在的就是能正常使用的,完整代码已更新到仓库。
  • 在父组件中写死list在data是不会渲染该瀑布流组件的,因为组件内部是通过watch监听的方式更新瀑布流列表,只要list有更新就会正常渲染的
  • 本人也在用这个组件,发现问题都会持续更新,如果发现什么问题可以留言或联系我。

最后

以上就是贤惠大叔最近收集整理的关于手把手写一个uniapp小程序瀑布流组件子组件父组件的全部内容,更多相关手把手写一个uniapp小程序瀑布流组件子组件父组件内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(77)

评论列表共有 0 条评论

立即
投稿
返回
顶部