T O P

[资源分享]     vue实现网络图片瀑布流 + 下拉刷新 + 上拉加载更多

  • By - 楼主

  • 2020-01-14 00:21:21
  • 一、思路分析和效果图

      用vue来实现一个瀑布流效果,加载网络图片,同时有下拉刷新和上拉加载更多功能效果。然后针对这几个效果的实现,捋下思路:

    1. 根据加载数据的顺序,依次追加标签展示效果;
    2. 选择哪种方式实现瀑布流,这里选择绝对定位方式;
    3. 关键问题:由于每张图片的宽高不一样,而瀑布流中要求所有图片的宽度一致,高度随宽度等比缩放。而且由于图片的加载是异步延迟。在不知道图片高度的情况下,每个图片所在的item盒子不好绝对定位。因此在渲染页面前先获取所有图片的高度,是解决问题的关键点!这里选择用JS中的Image类,通过预加载图片的方式提前获取图片宽高,另外通过一个临时变量来计算是否所有图片的高度已经得到。当所有的图片高度获取后,开始渲染页面。
    4. 页面渲染后,获取所有图片所在的盒子,循环计算盒子的高度,开始设置每个盒子item的绝对定位。
    5. 页面渲染时,会出现闪烁的现象。如何解决这个问题呢?这里用了一个动画样式。不过在第一次加载的时候,还是会有一点闪烁的感觉。
    6. 然后就是下拉刷新和上拉加载更多的效果,这里用了有赞的vant组件PullRefresh和List这套组合组件来实现。

    先看个效果动图:

    静态截图:

     

    二、具体实现步骤

    2.1、页面结构设计,测试数据准备。

       本地准备一个json文件数据,放在项目public文件夹下。注意,本地测试数据必须放在public文件夹下,网络请求时才能请求到数据,这是vue3.x。新增加一个axios依赖包,用来进行网络请求。部分截图,及关键代码:

    //数据请求
    getDataList(){
        this.$axios.get("/json/dataList.json").then((res)=>{
    
             let list = res.data.data ? res.data.data: [];
             if (list.length > 0){
                 //从list中取pageSize条数据出来
                 var tempList = [];
                  for (let i = 0; i < this.pageSize; i++){
                       if (list.length > 0){
                          let tempIndex = parseInt(Math.random() * 1000) % list.length;
                          tempList.push(list[tempIndex]);
                          list.splice(tempIndex, 1);
                      }
                  }
                   this.loadImagesHeight(tempList); //模拟预加载图片,获取图片高度
              }
              else {
                  this.loadImagesHeight(list); 
              }
          }).catch((res)=>{
               console.log("..fail: ", res);
               this.$toast.clear();
               this.isLoading = false; //下拉刷新请求完成
               this.loading = false; //上拉加载更多请求完成
         })
    },

     

    2.2、预加载图片,存储图片高度

      获取数据后,遍历数据数组,预加载图片,计算图片缩放后的高度,存储起来。同时由于图片加载是异步加载,所以用变量计数,当最后一个图片加载完成后,开始渲染页面。

    loadImagesHeight(list){
                    var count = 0; //用来计数,表示是否所有图片高度已经获取
                    list.forEach((item, index)=>{
                        //创建图片对象,加载图片,计算图片高度
                        var img = new Image();
                        img.src = item.cover;
                        img.onload = img.onerror = (e)=>{
                            count++;
                            if (e.type == 'load'){ //图片加载成功
                                //计算图片缩放后的高度:图片原高度/原宽度 = 缩放后高度/缩放后宽度
                                list[index].imgHeight = Math.round(img.height * this.boxWidth / img.width);
                                // console.log('index: ', index, ', load suc, imgHeiht: ', list[index].imgHeight);
                            }
                            else{ //图片加载失败,给一个默认高度50
                                list[index].imgHeight = 50;
                                console.log("index: ", index, ", 加载报错:", e);
                            }
    
                            //加载完成最后一个图片高度,开始下一步数据处理
                            if (count == list.length){
                                this.resolveDataList(list);
                            }
                        }
                    })
    },
    View Code

     

    2.3、渲染页面,设置绝对定位

      所有图片通过预加载获取图片高度后,开始渲染页面。然后遍历所有图片所在盒子标签,获取盒子高度,设置每个盒子的绝对定位。

    resolveDataList(list){ //处理数据
                    //下拉刷新,清空原数据
                    if (this.pageIndex <= 1){
                        this.itemCount = 0;
                        this.dataList = [];
                        this.lastRowHeights = [0, 0]; //存储每列的最后一行高度清0
                    }
    
                    if (list.length >= this.pageSize){
                        this.pageIndex++;  //还有下一页
                    }
                    else{
                        this.finished = true; //当前tab类型下所有数据已经加载完成
                    }
    
                    //合并新老两个数组数据
                    this.dataList = [...this.dataList, ...list];
                    //判断页面是否有数据
                    this.haveData = this.dataList.length > 0 ? 2 : 1;
                    this.isLoading = false; //下拉刷新请求完成
                    this.loading = false; //上拉加载更多请求完成
    
                    console.log("...datalist: ", this.dataList);
                    console.log("...this.isLoading: ", this.isLoading)
    
                    this.$nextTick(()=>{
                        setTimeout(()=>{
                            //渲染完成,计算每个item宽高,设置标签坐标定位
                            this.setItemElementPosition();
                            this.isLoading = false; //下拉刷新请求完成
                            this.loading = false; //上拉加载更多请求完成
                        }, 1000)
                    });
                },
                //获取每个item标签高度,设置item的定位
                setItemElementPosition(){
                    let parentEle = document.getElementById('data-list-box');
                    let boxEles = parentEle.getElementsByClassName("data-item");
    
                    for (let i = this.itemCount; i < boxEles.length; i++){
                        let tempEle = boxEles[i];
                        //上一个标签最小高度的列索引
                        let curColIndex = this.getMinHeightIndex(this.lastRowHeights);
                        let boxTop = this.lastRowHeights[curColIndex] + this.boxMargin;
                        let boxLeft = curColIndex * (this.boxWidth + this.boxMargin) + this.boxMargin;
                        tempEle.style.left = boxLeft + 'px';
                        tempEle.style.top = boxTop + 'px';
                        this.lastRowHeights[curColIndex] = boxTop + tempEle.offsetHeight;
    
                        // console.log('i = ', i, ', boxTop: ', boxTop, ', eleHeight: ', tempEle.offsetHeight);
                    }
    
                    this.itemCount = boxEles.length;
    
                    //修改父级标签的高度
                    let maxHeight = Math.max.apply(null, this.lastRowHeights);
                    parentEle.style.height = maxHeight + 'px';
    
                    this.$toast.clear();
                    console.log("...boxEles: ", boxEles.length, ", maxH: ", maxHeight);
                },
    View Code

     

    2.4、其他说明

      其他页面中如下拉刷新,和上拉加载更多等功能,使用了中的 和 这一套组合组件。感觉效果挺棒的,使用步骤也简单。另外就是在页面渲染时,会出现页面闪烁的现象,后面使用了一个css动画处理了这个现象,效果好了很多。但是在第一次加载的时候,还是有轻微的闪烁现象。等后面找到更好的方法,再更新。

     

    完整效果DEMO地址:

    本帖子中包含资源

    您需要 登录 才可以下载,没有帐号?立即注册


Image

test003 1

demo 地址呢,兄弟