1、什么是瀑布流

1.1、瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,图片的宽度是固定的,高度自动。视觉表现为参差不齐的多列布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。

2、h5下实现一个瀑布流的基本思路

2.1、定义基本的html结构

<div class="container">
     <div class="list">
         <img src="" alt="">
     </div>
     <div class="list">
         <img src="" alt="">
     </div>
</div>

2.2、定义基本的css样式

.container{
.container{width: 100%;padding: 10px 10px;position: relative;}
.list{float: left;width: 100px;padding: 10px;bottom: 10px;}
.list img{width: 100px;}

2.3、js动态计算加载项的样式。

function water_fall(parent_selector,box_selector){
    let boxElem=document.getElementsByClassName(box_selector);
    let boxWidth=boxElem[0].offsetWidth;
    let cols=2;
    let height_arr=[];
    for(let i=0;i<boxElem.length;i++){
        let cur_box=boxElem[i];
        let cur_box_height=cur_box.offsetHeight;
        if(i<cols){
            height_arr.push(cur_box_height);
        }else{
            let minH = Math.min.apply(null, height_arr);
            let index=height_arr.indexOf(minH);
            cur_box.style.cssText+=`;position: absolute;top:${minH}px;left:${boxWidth*index}px`;
            height_arr[index]+=cur_box_height;
        }
    }
    var maxH = Math.max.apply(null, height_arr);
    let parentElem=document.getElementById(parent_selector);
    parentElem.style.cssText+=`;height: ${maxH}px`;
}

这里固定2列,通过定义一个高度数组,然后遍历区块,获取每个列表项的高度,然后push到数组,从数组中找到最短的高度所在的位置来决定后面的列表加入到哪一列,并且设置它的left和top。到此一个简单的瀑布流已经完成,上面的代码其实还可以优化。每次只需遍历新加载的数据,然后追加到父节点中。
效果图如下:

clipboard.png

或者微信打开:https://wx54540d14c1b298c1.mg...

3、如何在微信小程序下实现一个瀑布流。

说了这么多,其实无非是想接下来做一个对比,来看下微信小程序下怎么实现一个瀑布流。
还是分3步
3.1、定义基本的wxml结构

<view class="content_list">
    <view class="list ">
        <image class="img_item " src="xxx"/>
    </view>
     <view class="list ">
        <image class="img_item " src="xxx"/>
    </view>
</view>

3.2、定义基本的wxss样式

.content_list{position: relative;}
.list{width: 350rpx;min-height: 200rpx;}
.img_item{width: 100%;}

这里有个差别,就是我没有给.list这个class设置padding了,因为在微信小程序下是不能够操作节点获取样式的。
后面我们将根据图片的宽度动态计算左右两边以及左边一列图片的padding。同时这里图片的单位用的是rpx。主要是为了适应不同屏幕终端。
3.3、js动态计算加载项的样式。
两种方案,第一种定义一个隐藏域,用于存放图片,当图片加载的时候绑定加载事件获取图片的宽高

    <view style="display:none">
        <image  wx:for="{{temImgArr}}" wx:key="id" id="{{item.id}}" binderror="onImageError" src="https:{{item.link}}" bindload="onImageLoad"></image>
    </view>

主要js代码如下:

onImageLoad: function (e) {
        let imageId = e.currentTarget.id;
        let oImgW = e.detail.width;         //图片原始宽度
        let oImgH = e.detail.height;        //图片原始高度
        let imgWidth = (this.data.winWidth - 20) * 0.48;
        let scale = imgWidth / oImgW;        //比例计算
        let imgHeight = scale * oImgH;
        let imgObj = {
            id: imageId,
            width: imgWidth,
            height: imgHeight
        };
        imgLen++;
        for (let i = 0; i < temResImgArr.length; i++) {
            if (temResImgArr[i].id == imageId) {
                temResImgArr[i].width = imgWidth;
                temResImgArr[i].height = imgHeight;
                break;
            }
        }
        if (imgLen == temResImgArr.length) {//图片遍历完
            this.waterFall();
        }

    },
    onImageError: function (e) {
        imgLen++;
    },
     waterFall: function () {
        for (let i = 0; i < temResImgArr.length; i++) {
            if (heightArr.length < 2 && i < 2) {
                heightArr.push(temResImgArr[i].height + 10);
            } else {
                let minH = Math.min.apply(null, heightArr);
                let index = heightArr.indexOf(minH);
                temResImgArr[i].top = `${minH}`;
                temResImgArr[i].left = `${360 * index}rpx`;
                heightArr[index] += (temResImgArr[i].height + 10);
            }
        }
        let maxH = Math.max.apply(null, heightArr);
        let temp = this.data.imgArr;
        temp.push(...temResImgArr);
        this.setData({
            imgArr: temp,
            viewHeight: maxH,
            temImgArr: []
        });
        //重置数据。
        temResImgArr = [];
        imgLen = 0;
        wx.hideToast();
    },

但是这种方案并不是最优,需要定义2个临时数组来处理加载的图片,同时用户等待的时间太长,必须要等所有图片加载完后获取到所有的高度后才能够展示出来,体验很不好。
既然花了大部分时间在获取图片宽高上面,那么为什么不能够从接口输出图片宽高呢?
所以要么上传图片的时候把宽高录入db,但是这种并没有什么意义。要么就是输出的时候处理,这时想到了PHP有个getimagesize函数(PHP是世界上最好的语言有木有),可以获取到图片的宽高。这样就不用改db了。
备注:这里更正下,组内大神一眼就看出输出时候用getimagesize函数处理存在性能问题,就是高并发的时候、服务器带宽很容易耗尽,不同进程之间拉取同样图片还无法利用缓存。最后还是改为将图片宽高存入后台。
且看改进后的js代码:

 onLoad: function () {
        let self = this;
        imgLen = 0;
        heightArr = [];
        wx.getSystemInfo({
            success: function (res) {
                let imgW = Math.floor(350 * (res.windowWidth) / 750);//图片在当前屏幕尺寸下的实际宽度
                let colW = Math.floor((res.windowWidth - 2 * imgW) / 3);//左右两边边距和图片边距的宽度
                self.setData({
                    winWidth: res.windowWidth,
                    winHeight: res.windowHeight,
                    colW: colW,
                });
                self.getImgInfo();
            }
        })
    },
waterFall: function (data) {
        let j = 0;
        for (let i = 0; i < data.length; i++) {//遍历动态加载的数据
            let imgW = Math.floor(350 * this.data.winWidth / 750);//获取图片在当前屏幕下的实际宽度
            data[i].height = Math.floor(imgW * data[i].height / data[i].width);
            if (heightArr.length < 2 && i < 2) {
                heightArr.push(data[i].height + this.data.colW);//实际高度+动态边距
                data[i].top = `0`;
                data[i].left = i == 0 ? `${imgW * i + this.data.colW}` : `${imgW * i + 2 * this.data.colW}`;
            } else {
                let minH = Math.min.apply(null, heightArr);
                let index = heightArr.indexOf(minH);
                data[i].top = `${minH}`;
                data[i].left = index == 0 ? `${imgW * index + this.data.colW}` : `${imgW * index + 2 * this.data.colW}`;
                heightArr[index] += (data[i].height + this.data.colW);
            }
        }
        let maxH = Math.max.apply(null, heightArr);
        let temp = this.data.imgArr;
        temp.push(...data);//追加到当前图片列表中
        this.setData({
            imgArr: temp,
            viewHeight: maxH,
        });
        //重置数据。
        temp=null;
        imgLen = 0;
        wx.hideToast();
    },

实际效果如下:

clipboard.png

这样就实现了一个微信小程序下的瀑布流。实际效果可以打开微信,扫描左边二维码,直接体验。或者微信小程序搜索拍照POSE大全。里面附很多美图,各种拍照姿势等你来完善,来补充,欢迎体验并上传自己的小姿势。
PS:无双不成对,一张图太单调,请容许我再附上最近做的一款美的砍价小程序,美的认证,砍到即可购买。最近天气热,有购买家电的朋友可以扫码购买。

图片描述图片描述

最后,如果你有更优的解决方案请告诉我,我们一起探讨,欢迎点评!

本文固定链接: http://www.js-code.com/weixin/weixin_45925.html