uniapp小程序瀑布流升级版

uniapp小程序瀑布流升级版

DansRoh Lv4

前言

上文使用双列布局实现了简单版的瀑布流,但是在小程序上面由于css不兼容,以及一次请求数据过多,图片高度不确定,会导致下滑加载数据过程中,页面上下抽搐,卡顿。
于是我换了一种相对复杂的方式,来完成小程序瀑布流的实现。

实现思路

  • 使用float布局,分为左侧和右侧两个列表
  • 在请求拿到分页数据后,遍历数据,计算leftBox 和 rightBox的高度。
  • 比较高度,将数据item push到高度小的那个盒子

代码

  • 新建WaterfallList文件夹,及文件夹下的index.vue文件

    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
    <template>
    <view class="waterfallList">
    <view id="leftList" class="leftList">
    <ProductItem v-for="item in leftData" :key="item.id" :productData="item"></ProductItem>
    </view>
    <view id="rightList" class="rightList">
    <ProductItem v-for="item in rightData" :key="item.id" :productData="item"></ProductItem>
    </view>
    </view>
    </template>

    <script>
    import ProductItem from "./ProductItem.vue";

    export default {
    components: {ProductItem},
    data() {
    return {
    leftData: [],
    rightData: []
    };
    },
    methods: {
    async loadMore(data) {
    for (const item of data) {
    // 获取高度
    const leftListHeight = await this.getDomHeight('#leftList')
    const rightListHeight = await this.getDomHeight('#rightList')

    if (leftListHeight < rightListHeight) {
    this.leftData.push(item)
    } else {
    this.rightData.push(item)
    }
    }
    },
    getDomHeight(selector) {
    return new Promise(resolve => {
    uni.createSelectorQuery().in(this).select(selector).fields(
    {
    size: true,
    },
    (data) => {
    resolve(data.height)
    }
    ).exec();
    })
    }
    }
    }
    </script>

    <style lang="scss" scoped>
    .waterfallList {
    overflow: hidden;
    }
    .leftList {
    width: 48%;
    float: left;
    }
    .rightList {
    width: 48%;
    float: right;
    }
    </style>
  • ProductItem组件

    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
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    <template>
    <view class="productItem">
    <image class="productImage" :src="productData.pic" lazy-load="true" mode="widthFix"></image>
    <view class="container">
    <view class="productName">{{ productData.name }}</view>
    <view class="priceBox">
    <view>
    <text class="priceIcon">¥</text>
    {{ productData.price }}
    <text class="unit" v-if="productData.tag">/箱</text>
    </view>
    <view class="iconBox">
    <uni-icons color="#fff" type="cart" size="32rpx"></uni-icons>
    </view>
    </view>
    <view v-if="productData.tag" class="tag">
    <view class="iconBoxTag">
    <image class="medalsIcon" :src="getStatic('/index/medals.png')" mode="scaleToFill"></image>
    </view>
    <view class="tagText">{{productData.tag}}</view>
    <view class="tagOrder">TOP 1</view>
    </view>
    </view>
    </view>
    </template>

    <script>
    import {getStatic} from "@/utils/global";

    export default {
    methods: {getStatic},
    props: {
    productData: {
    type: Object,
    default: {}
    }
    },
    data() {
    return {};
    }
    }
    </script>

    <style lang="scss" scoped>
    .productItem {
    width: 344rpx;
    border-radius: 15rpx;
    overflow: hidden;
    background-color: #fff;
    margin-bottom: 20rpx;
    break-inside: avoid;
    }
    .productItem .productImage {
    width: 344rpx;
    }

    .container {
    padding: 0 22rpx 30rpx 22rpx;
    font-size: 24rpx;

    .productName {
    color: #212121;
    font-size: 28rpx;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    overflow:hidden;
    /*! autoprefixer: off */
    -webkit-box-orient: vertical;
    }

    .priceBox {
    color: #ff0000;
    font-size: 40rpx;
    line-height: 32rpx;
    font-weight: bold;
    padding-top: 22rpx;
    display: flex;
    justify-content: space-between;
    align-items: center;

    .priceIcon {
    font-size: 26rpx;
    }
    .unit {
    font-size: 28rpx;
    color: #CFCFCF;
    }
    .iconBox {
    width: 52rpx;
    height: 52rpx;
    border-radius: 50%;
    overflow: hidden;
    background: linear-gradient( 180deg, #FA2C19 0%, #FE5617 100%);
    display: flex;
    justify-content: center;
    align-items: center;
    }
    }
    .tag {
    margin-top: 20rpx;
    width: 304rpx;
    height: 48rpx;
    background: #FAF5EC;
    border-radius: 8rpx;
    display: flex;
    align-items: center;
    .iconBoxTag {
    width: 52rpx;
    height: 48rpx;
    overflow: hidden;
    background: linear-gradient( 166deg, #E3CB8B 0%, #D5A443 100%);
    border-radius: 8rpx 0rpx 8rpx 8rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    .medalsIcon {
    width: 36rpx;
    height: 36rpx;
    }
    }
    .tagText{
    margin-left: 16rpx;
    font-weight: 500;
    font-size: 26rpx;
    color: #C89839;
    }
    .tagOrder {
    margin-left: 8rpx;
    font-size: 26rpx;
    color: #C89839;
    font-weight: 500;
    }
    }
    }
    </style>
  • 在index中引入WaterfallList组件,通过this.$refs调用WaterfallList组件的loadMore方法,将请求的分页数据传入

    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
    // 下滑加载推荐商品数据
    <template>
    <WaterfallList ref="waterfallRef"></WaterfallList>
    </template>

    <script >
    import WaterfallList from '@/components/WaterfallList/index.vue'
    export default {
    components: {
    WaterfallList
    },
    data() {
    return {
    pageNum: 1,
    loadingType: 'more'
    }
    },
    methods: {
    async loadListData() {
    if (this.loadingType === 'more') {
    this.loadingType = 'loading'
    // 请求数据
    const { data } = await fetchProductList({pageNum: this.pageNum, pageSize:10})
    if (data.length === 0) {
    this.loadingType = 'nomore'
    return
    }
    // 调用loadMore更新列表
    await this.$refs.waterfallRef.loadMore(data)
    this.pageNum++
    this.loadingType = 'more'
    }
    },
    }
    }
    </script>
  • 标题: uniapp小程序瀑布流升级版
  • 作者: DansRoh
  • 创建于 : 2024-09-10 00:00:00
  • 更新于 : 2024-09-10 13:31:07
  • 链接: https://blog.shinri.me/2024/09/10/41_uniapp小程序实现瀑布流升级版/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
此页目录
uniapp小程序瀑布流升级版