Skip to content

Waterfall 瀑布流组件

这是一个瀑布流形式的组件,内容分为左右两列,结合uview的懒加载组件效果更佳。相较于某些只是奇偶数左右分别,或者没有利用vue作用域插槽的做法,uView的瀑布流实现了真正的 组件化,搭配LazyLoad 懒加载和loadMore 加载更多组件,让您开箱即用,眼前一亮。

提示

由于右侧的演示是通过iframe标签引入的,缺少了手机端运行的相关API,或者因为演示区域太小,或者电脑分别率不够高 ,导致演示可能会有问题,手机端有不会这些问题,请在右上角的"演示"中用手机扫码查看对应的效果。

📌平台差异说明

APP(vue)H5微信小程序支付宝小程序

🏯基本使用示例

html
<!-- 全局使用 -->
<hy-waterfall v-model="flowList"></hy-waterfall>
ts
const flowList = [...]

完整应用示例

html
<template>
    <view class="waterfall">
        <hy-waterfall v-model="flowList" ref="waterfallRef">
            <template v-slot:left="{ leftList }">
                <view class="demo-water" v-for="(item, index) in leftList" :key="index">
                    <!-- 微信小程序需要hx2.8.11版本才支持在template中引入其他组件,比如下方的u-lazy-load组件 -->
                    <hy-image width="100%" mode="widthFix" radius="5" :src="item.image"></hy-image>
                    <view class="demo-title">{{ item.title }}</view>
                    <view class="demo-price">{{ item.price }}元</view>
                    <view class="demo-tag">
                        <view class="demo-tag-owner">
                            <text class="text">自营</text>
                        </view>
                        <view class="demo-tag-text">
                            <text class="text">放心购</text>
                        </view>
                    </view>
                    <view class="demo-shop">{{ item.shop }}</view>
                    <view class="u-close">
                        <up-icon
                            name="close-circle-fill"
                            color="#fa3534"
                            size="16"
                            @click="remove(item.id)"
                        ></up-icon>
                    </view>
                </view>
            </template>
            <template v-slot:right="{ rightList }">
                <view class="demo-water" v-for="(item, index) in rightList" :key="index">
                    <hy-image width="100%" mode="widthFix" radius="5" :src="item.image"></hy-image>
                    <view class="demo-title">{{ item.title }}</view>
                    <view class="demo-price">{{ item.price }}元</view>
                    <view class="demo-tag">
                        <view class="demo-tag-owner">
                            <text class="text">自营</text>
                        </view>
                        <view class="demo-tag-text">
                            <text class="text">放心购</text>
                        </view>
                    </view>
                    <view class="demo-shop">{{ item.shop }}</view>
                    <view class="u-close">
                        <up-icon
                            name="close-circle-fill"
                            color="#fa3534"
                            size="34"
                            @click="remove(item.id)"
                        ></up-icon>
                    </view>
                </view>
            </template>
        </hy-waterfall>
    </view>
</template>

<script setup lang="ts">
    import { onMounted, ref } from 'vue';
    import { guid, random } from 'hy-app';
    import list from './index';
    import { onReachBottom } from '@dcloudio/uni-app';

    const flowList = ref<Record<string, any>[]>([]);
    const waterfallRef = ref();
    const loadStatus = ref('loadmore');

    onMounted(() => {
        addRandomData();
    });
    onReachBottom(() => {
        loadStatus.value = 'loading';
        // 模拟数据加载
        setTimeout(() => {
            addRandomData();
            loadStatus.value = 'loadmore';
        }, 1000);
    });

    const addRandomData = () => {
        for (let i = 0; i < 20; i++) {
            let index = random(0, list.length - 1);
            // 先转成字符串再转成对象,避免数组对象引用导致数据混乱
            let item = JSON.parse(JSON.stringify(list[index]));
            item.id = guid();
            flowList.value.push(item);
        }
    };
    const remove = (id: string) => {
        waterfallRef.value.remove(id);
    };
    const clear = () => {
        waterfallRef.value.clear();
    };
</script>

<style scoped lang="scss">
    @import './index.scss';
</style>
javascript
export default [
    {
        price: '1300万',
        title: '法拉利LaFerrari',
        shop: '性能超群,领跑超级跑车界',
        image: 'https://pic.rmb.bdstatic.com/bjh/news/68eb7f6d762684a7f81d91edd1190cde.jpeg',
    },
    {
        price: '3300万',
        title: '布加迪 Chiron',
        shop: '性能超群,领跑超级跑车界',
        image: 'https://img1.baidu.com/it/u=710933577,1433411533&fm=253',
    },
    {
        price: '2000万',
        title: '法拉利296 GTS',
        shop: '新时代混动法拉利敞篷跑车',
        image: 'https://pic.rmb.bdstatic.com/bjh/240514/news/3ed50c020490bdf65832b56bfdec9edf6399.jpeg',
    },
    {
        price: '784万',
        title: '柯尼塞格Jesko',
        shop: '一头拥有超强马力和极致速度的“猛兽”',
        image: 'https://img2.baidu.com/it/u=2465835289,2125464963&fm=253',
    },
    {
        price: '9000万',
        title: '兰博基尼Temerario',
        shop: '李白杜甫白居易旗舰店',
        image: 'https://pic.rmb.bdstatic.com/da708645a2bce38179625e8364c05a76.jpg@h_1280',
    },
    {
        price: '400万',
        shop: '保时捷963',
        title: '保时捷963',
        image: 'https://img0.baidu.com/it/u=3402753783,3081776286&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500',
    },
    {
        price: '233万',
        shop: '玛莎拉蒂Gran Turismo',
        title: '玛莎拉蒂Gran Turismo',
        image: 'https://img1.baidu.com/it/u=1729207840,2645487814&fm=253&fmt=auto?w=800&h=1002',
    },
    {
        price: '1654万',
        title: '阿斯顿马丁',
        shop: '李白杜甫白居易旗舰店',
        image: 'https://img1.baidu.com/it/u=3621246908,3142164736&fm=253',
    },
    {
        price: '1678万',
        title: '迈凯伦',
        shop: '李白杜甫白居易旗舰店',
        image: 'https://img0.baidu.com/it/u=2821221440,1024182410&fm=253',
    },
    {
        price: '924万',
        title: '兰博基尼 电鳗',
        shop: '李白杜甫白居易旗舰店',
        image: 'https://inews.gtimg.com/om_bt/Opd--mKoy4ZOIDzMAOz2bsY6i1cxTSHtREhKoNX2CaBY4AA/641',
    },
    {
        price: '8243万',
        title: '克尔维特',
        shop: '克尔维特',
        image: 'https://b0.bdstatic.com/b72b6ed36682acf2aad6f0fa853111c5.jpeg',
    },
];
scss
.waterfall {
    background-color: #f0e6ef;
}
.demo-water {
    border-radius: 8px;
    margin: 5px;
    background-color: #ffffff;
    padding: 8px;
    position: relative;
    /* #ifdef H5 */
    cursor: pointer;
    /* #endif */
    .u-close {
        position: absolute;
        top: -7px;
        right: 3px;
        opacity: 0;
    }
    /* #ifdef H5 */
    &:hover {
        .u-close {
            opacity: 1;
        }
    }
    /* #endif */
}

.demo-img-wrap {
}

.demo-image {
    width: 100%;
    border-radius: 4px;
}

.demo-title {
    font-size: 30rpx;
    margin-top: 5px;
    color: $u-main-color;
    /* #ifndef APP-NVUE */
    word-break: break-all;
    /* #endif */
}

.demo-tag {
    display: flex;
    flex-direction: row;
    margin-top: 5px;
}

.demo-tag-owner {
    background-color: $u-error;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2px 7px;
    border-radius: 20px;
    line-height: 1;
    .text {
        font-size: 12px;
        color: #ffffff;
    }
}

.demo-tag-text {
    border: 1px solid $u-primary;
    margin-left: 10px;
    border-radius: 50rpx;
    line-height: 1;
    padding: 2px 7px;
    border-radius: 20px;
    display: flex;
    align-items: center;
    border-radius: 20px;
    .text {
        font-size: 12px;
        color: $u-primary;
    }
}

.demo-price {
    font-size: 30rpx;
    color: $u-error;
    margin-top: 5px;
}

.demo-shop {
    font-size: 22rpx;
    color: $u-tips-color;
    margin-top: 5px;
}

注意事项

组件有一个add-time参数,用于将单条数据添加到队列的时间间隔,因为图片加载是需要时间的,所以瀑布流左右列 的高度会不定时改变,add-time值越大,对程序效果越好,但是对用户来说,越大值可能就是以能感受的速度一个一个添加 到队列尾部的,所以这是一个双面性的结果。

API

Waterfall Props

参数说明类型默认值
v-model接收列表值array-
addTime单条数据添加到队列的时间间隔,单位msnumber200
idKey数据的唯一值的键名stringid

Methods

事件名说明传参
clear清空列表数据-
remove删除某条数据id:唯一值,需要和idKey一致

Slots

插槽名说明接收值
left左边内容插槽-
right右边内容插槽-
03:29