UNIAPP 实现瀑布流列表

1,438 阅读1分钟

初衷以及大致的思路

前几个月公司需要整一个瀑布流的列表,看了下社区里面的瀑布流列表,感觉多多少少好像有点问题,于是就自己动手整了个,目前适配了ios和安卓、H5,微信小程序还没测。。。所以不知道咋样。。。目前还是挺好用的,H5直接用ref可以省下不少事件,但是app端炸了,所以放弃了ref,等待DOM生成后再来插入下一条数据

代码实现

通过flex,实现最基本两列布局,然后通过追加数组的方式,一条条插入左右两列,分别为list0list1

最开始的两条数据,我们可以直接分配位置

先计算所有这条数据需要异步加载资源的图片高度(正常只有一张图片),然后获取到这些高度后,直接给定高度,防止图片撑开后导致回流。

每次追加数据,获取当前list0和list1的高度,比较两边高度,插入最短的列表即可...

大致代码如下:

<template>
	<view>
		<view class="skeleton" v-if="initLoading">
			<view class="skeleton-item" v-for="item in 6" :key="item">
				<u-skeleton loading titleWidth="100%" :titleHeight="200"></u-skeleton>
				<u-skeleton class="mt8" loading titleWidth="100%" :titleHeight="20"></u-skeleton>
				<u-skeleton class="mt8" loading titleWidth="100%" :titleHeight="20"></u-skeleton>
				<u-skeleton class="mt8" titleWidth="100%" loading avatarSize="20" avatar></u-skeleton>
			</view>
		</view>
		<view class="case-list">
			<view v-for="(item, index) in list" :key="index" class="case-list-container">
				<view :id="`list${index}`">
					<view
						v-for="(childrenItem, childrenIndex) in item"
						:key="childrenIndex"
						class="case-list-item"
					>
                                        // 你的内容组件
					</view>
				</view>
			</view>
		</view>
		<u-loading-icon v-show="loading" mode="circle" text="正在获取数据中..."></u-loading-icon>
	</view>
</template>

<script>
import { getBlendRecommend } from '@/apis/article'
export default {
	data() {
            return {
                list: [[], []],
                loading: false,
                params: {},
            }
	},
	computed: {
            initLoading() {
                if (this.loading && !this.list[0].length && !this.list[1].length) {
                        return true
                }
                return false
            },
            windowWidth() {
                    // 24 - 左右边距总和
                    const { windowWidth } = uni.getSystemInfoSync()
                    return Number.parseInt(windowWidth / 2) - 24
            },
	},
	methods: {
            // 骨架屏,可以忽略
            setLoading(status) {
                switch (status) {
                    case 'waiting':
                            this.loading = true
                            break
                    case 'end':
                            this.loading = false
                            break
                }
            },
            // 获取数据
            getList() {
                if (this.loading) {
                    return false
                }
                this.setLoading('waiting')
                getBlendRecommend(this.params)
                    .then(data => { 
                        this.params.page++
                        if (Array.isArray(data) && data.length) {
                            let len = 0
                            // 获取图片的高度
                            data.forEach(item => {
                                    uni.getImageInfo({
                                        src: item.imgUrl,
                                        success: e => {
                                                item._height = Math.round(this.windowWidth * (e.height / e.width))
                                                const data = JSON.parse(JSON.stringify(item))
                                                // 加入队列
                                                this.handleRearrangement(data)
                                        },
                                        complete: () => {
                                                len++
                                                if (len === data.length) {
                                                        this.setLoading('end')
                                                }
                                        },
                                        fail: () => {
                                                len++
                                        },
                                    })
                            })
                        } else {
                            this.setLoading('end')
                        }
                        })
                        .catch(() => {
                            this.setLoading('end')
                        })
            },
            // 计算当前一列的长度 0左  1右
            getListHeight() {
                const leftList = new Promise(resolve => {
                    this.$nextTick(() => {
                        const query = uni.createSelectorQuery().in(this)
                        query
                                .select('#list0')
                                .boundingClientRect(data => {
                                        resolve(data.height)
                                })
                                .exec()
                    })
                })
                const rightList = new Promise(resolve => {
                    this.$nextTick(() => {
                        const query = uni.createSelectorQuery().in(this)
                        query
                            .select('#list1')
                            .boundingClientRect(data => {
                                    resolve(data.height)
                            })
                            .exec()
                    })
                })
                return Promise.all([leftList, rightList]).then(data => {
                    return data
                })
            },
            handleRearrangement(row) {
                this.getListHeight().then(res => {
                    const i = res[0] > res[1] ? 1 : 0
                    row.currentHeight = res
                  
                    const index = this.list[i].length - 1 >= 0 ? this.list[i].length - 1 : 0
                    // 如果高度无变化,说明DOM并未生成,给到下次继续判断,不追加数据
                    if (index && this.list[i][index].currentHeight[i] === res[i]) {
                        setTimeout(() => {
                            this.handleRearrangement(row)
                        }, 100)
                    } else {
                      // list0和list1的第一条数据,直接放入
                        this.list[i].push(row)
                    }
                })
            },
            handleRefresh() {
                this.getList()
            },
	},
	created() {
            this.getList()
	},
}
</script>