uniapp-小程序-瀑布流布局-支持下拉刷新上拉加载更多

uniapp-小程序-瀑布流布局-支持下拉刷新上拉加载更多

主要思路:

  • 将父组件传递进来的list分为两组,leftList和rightList,初始化为空数组
  • 页面布局分为左右两侧,左侧渲染leftList的数据,右侧渲染rightList的数据,并初始化左右两栏的高度为0
  • 利用image标签中的load事件,在图片加载的时候,获取到图片的实际尺寸,计算出要显示的高度。比较两栏的高度,将对应的数据加到leftList或者rightList

我们在组件中写一个两列的布局,图片的宽度设定为345rpx,文字的高度定为100rpx,下边距定为20rpx,给image绑定一个load方法,onImageLoad,这个组件的核心就是这个方法。

//wterfall
<template>
    <view class="waterfall">
        <view class="left">
            <block v-for="(item, index) in leftList" :key="index">
                <view class="waterfall-item">
                    <image :src="item.src" mode="widthFix" lazy-load @load="onImageLoad"></image>
                    <view class="title">{{ item.title }}</view>
                </view>
            </block>
        </view>
        <view class="right">
            <block v-for="(item, index) in rightList" :key="index">
                <view class="waterfall-item">
                    <image :src="item.src" mode="widthFix" lazy-load @load="onImageLoad"></image>
                    <view class="title">{{ item.title }}</view>
                </view>
            </block>
        </view>
    </view>
</template>

<style lang="scss">
.waterfall {
    width: 100%;
    display: flex;
    justify-content: space-between;
    padding: 0 20rpx;
    box-sizing: border-box;
    .left,
    .right {
        .waterfall-item {
            width: 345rpx;
            margin-bottom: 20rpx;
            background-color: pink;
            box-sizing: border-box;
            image {
                width: 345rpx;
                display: block;
            }
            .title {
                width: 345rpx;
                height: 100rpx;
                overflow: hidden;
            }
        }
    }
}
</style>

可以看到,上面有两个view,分别是left和right,分别渲染的是leftList和rightList里面的数据,那么这两个数据是怎么来的呢?我们在组件中定义一个prop:

props: {
        list: {
            type: Array,
            default: []
        }
}

这个list是通过父组件传递过来的,也就是你原始的数据,我们在组件中再声明几个变量,并在created的时候,将list中的第一个数据存入到leftList中

data() {
        return {
            leftList: [],
            rightList: [],
            itemIndex: 0,
            leftHeight: 0,
            rightHeight: 0
        };
},
created() {
        this.leftList = [this.list[0]]; //第一张图片入栈
},

然后就是最重要的onImageLoad方法了

onImageLoad(e) {
            if (!e) {
                console.log('无图片!!!!');
                return;
            }
            let imgH = (e.detail.height / e.detail.width) * 345 + 100 + 20; //图片显示高度加上下面的文字的高度100rpx,加上margin-bottom20rpx
            let that = this;
            if (that.itemIndex === 0) {
                that.leftHeight += imgH; //第一张图片高度加到左边
                that.itemIndex++;
                that.rightList.push(that.list[that.itemIndex]); //第二张图片先入栈
            } else {
                that.itemIndex++;
                //再加高度
                if (that.leftHeight > that.rightHeight) {
                    that.rightHeight += imgH;
                } else {
                    that.leftHeight += imgH;
                }
                if (that.itemIndex < that.list.length) {
                    //下一张图片入栈
                    if (that.leftHeight > that.rightHeight) {
                        that.rightList.push(that.list[that.itemIndex]);
                    } else {
                        that.leftList.push(that.list[that.itemIndex]);
                    }
                }
            }
    }

上面注释的也比较清楚了,首先要将第一张图入栈,这个时候onImageLoad才会返回图片信息,不然的话是没有图片的,在onImageLoad方法执行的时候,先计算出图片的显示高度,然后计算leftHeight和rightHeight的高度,再将下一张图入栈。

上拉加载更多、下拉刷新

上拉加载更多的时候,监听list的变化,list的新的值会比旧的值length更大,这个时候将新添加进来的值的第一个数据先入栈,相当于是首次加载的时候第一个数据先放到leftList。

    watch: {
        list(n, o) {
            let that = this;
            // console.log('=====watch  list=====', n, o);
            let ol = o.length;
            let nl = n.length;
            if (nl > ol) {
                if (this.leftHeight > this.rightHeight) {
                    that.rightList.push(that.list[ol]);
                } else {
                    that.leftList.push(that.list[ol]);
                }
                this.onImageLoad();
            }
        }
    },

下拉刷新的时候有一点不一样,需要先将组件销毁掉,再重新渲染,可以在父组件中用v-if判断是否要销毁,父组件中的代码:

<waterFall v-if="goodsList.length > 0" :list="goodsList"></waterFall>

onPullDownRefresh() {
        this.goodsList = [];
        setTimeout(() => {
            this.goodsList = [...list];
        }, 500);

        uni.stopPullDownRefresh();
}

大致就是这样,我觉得还不够好,但在我的项目中也是够用了,贴上完整的代码:

//waterfall.vue
<template>
    <view class="waterfall">
        <view class="left">
            <block v-for="(item, index) in leftList" :key="index">
                <view class="waterfall-item">
                    <image :src="item.src" mode="widthFix" lazy-load @load="onImageLoad"></image>
                    <view class="title">{{ item.title }}</view>
                </view>
            </block>
        </view>
        <view class="right">
            <block v-for="(item, index) in rightList" :key="index">
                <view class="waterfall-item">
                    <image :src="item.src" mode="widthFix" lazy-load @load="onImageLoad"></image>
                    <view class="title">{{ item.title }}</view>
                </view>
            </block>
        </view>
    </view>
</template>

<script>
export default {
    name: 'water-fall',
    props: {
        list: {
            type: Array,
            default: []
        }
    },
    watch: {
        list(n, o) {
            let that = this;
            // console.log('=====watch  list=====', n, o);
            let ol = o.length;
            let nl = n.length;
            if (nl > ol) {
                if (this.leftHeight > this.rightHeight) {
                    that.rightList.push(that.list[ol]);
                } else {
                    that.leftList.push(that.list[ol]);
                }
                this.onImageLoad();
            }
        }
    },
    data() {
        return {
            leftList: [],
            rightList: [],
            itemIndex: 0,
            leftHeight: 0,
            rightHeight: 0
        };
    },
    created() {
        this.leftList = [this.list[0]]; //第一张图片入栈
    },
    destroyed() {
        console.log('destroy');
    },
    methods: {
        onImageLoad(e) {
            if (!e) {
                console.log('无图片!!!!');
                return;
            }
            let imgH = (e.detail.height / e.detail.width) * 345 + 100 + 20; //图片显示高度加上下面的文字的高度100rpx,加上margin-bottom20rpx
            let that = this;
            if (that.itemIndex === 0) {
                that.leftHeight += imgH; //第一张图片高度加到左边
                that.itemIndex++;
                that.rightList.push(that.list[that.itemIndex]); //第二张图片先入栈
            } else {
                that.itemIndex++;
                //再加高度
                if (that.leftHeight > that.rightHeight) {
                    that.rightHeight += imgH;
                } else {
                    that.leftHeight += imgH;
                }
                if (that.itemIndex < that.list.length) {
                    //下一张图片入栈
                    if (that.leftHeight > that.rightHeight) {
                        that.rightList.push(that.list[that.itemIndex]);
                    } else {
                        that.leftList.push(that.list[that.itemIndex]);
                    }
                }
            }
        }
    }
};
</script>

<style lang="scss">
.waterfall {
    width: 100%;
    display: flex;
    justify-content: space-between;
    padding: 0 20rpx;
    box-sizing: border-box;
    .left,
    .right {
        .waterfall-item {
            width: 345rpx;
            margin-bottom: 20rpx;
            background-color: pink;
            box-sizing: border-box;
            image {
                width: 345rpx;
                display: block;
            }
            .title {
                width: 345rpx;
                height: 100rpx;
                overflow: hidden;
            }
        }
    }
}
</style>

<template>
    <view>
        <waterFall v-if="goodsList.length > 0" :list="goodsList"></waterFall>
    </view>
</template>

<script>
const list = [
    { src: 'cardBg7.png', title: '1、非常好看的额图片,快起来这里买东西' },
    { src: 'ios.png', title: '2、这段文字要少' },
    { src: 'home_banner1.jpg', title: '3、大段文字展示' },
    { src: 'IMG17.jpeg', title: '4、特朗普的眼镜' },
    { src: 'cardBg5.png', title: '5、非常好看的额图片,快起来这里买东西' },
    { src: 'operation_pt_img.png', title: '6、非常好看的额图片,快起来这里买东西' }
];
import waterFall from '@/components/waterfall.vue';
export default {
    components: {
        waterFall
    },
    data() {
        return {
            goodsList: [...list] //商品列表
        };
    },
    onReachBottom() {
        if (this.goodsList.length > 29) {
            uni.showToast({
                title: '30条全部加载完毕!',
                icon: 'none'
            });
        } else {
            this.goodsList = this.goodsList.concat(list);
        }
    },
    onPullDownRefresh() {
        this.goodsList = [];
        setTimeout(() => {
            this.goodsList = [...list];
        }, 500);

        uni.stopPullDownRefresh();
    }
};
</script>
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,082评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,231评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,047评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,977评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,893评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,976评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,605评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,888评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,906评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,732评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,513评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,980评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,132评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,447评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,027评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,232评论 2 339