vue for Web picture waterfall streaming + drop-down refresh + pull-up load more

1. Idea analysis and effect diagram

Use vue to achieve a waterfall streaming effect, load network pictures, with drop-down refresh and pull-up load more features.Then, for the realization of these effects, put forward ideas:

  1. Labels are appended to show the effect in turn according to the order in which the data is loaded.
  2. Choose which way to achieve the waterfall flow, here choose the absolute positioning method;
  3. Key Issues: Because each picture has a different width and height, the waterfall flow requires all pictures to have the same width and height to scale with equal ratio of width.And because the loading of pictures is asynchronous delay.Without knowing the height of the picture, the item box in which each picture is located is not positioned absolutely.So getting the height of all the pictures before rendering the page is the key to solving the problem!Here you choose to use the Image class in JS to get the width and height of the picture in advance by pre-loading the picture, and a temporary variable to calculate if all the picture heights have been obtained.When all the picture heights are taken, the page is rendered.
  4. After rendering the page, get the boxes where all the pictures are located, cycle through the height of the boxes, and start setting the absolute positioning of each box item.
  5. Flashing occurs when a page is rendered.How can I solve this problem?An animation style is used here.But it still feels a little flickering the first time it loads.
  6. Then there's the drop-down refresh and pull-up load, which are done with the combination of the fantastic vant component PullRefresh and List.

First look at the effect map:

Static Screenshot:

 

2. Specific implementation steps

2.1. Page structure design, test data preparation.

Prepare a json file data locally and place it in the project public folder.Note that the local test data must be placed in the public folder for network requests to request data, which is vue3.x.Add a new axios dependency package for network requests.Partial screenshots, and key code:

//Data Request
getDataList(){
    this.$axios.get("/json/dataList.json").then((res)=>{

         let list = res.data.data ? res.data.data: [];
         if (list.length > 0){
             //from list Reclaim pageSize Bar data out
             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); //Simulate pre-loaded pictures to get their height
          }
          else {
              this.loadImagesHeight(list); 
          }
      }).catch((res)=>{
           console.log("..fail: ", res);
           this.$toast.clear();
           this.isLoading = false; //Drop-down refresh request complete
           this.loading = false; //Drop-up Load More Requests Completed
     })
},

 

2.2. Preload pictures, store picture height

After obtaining the data, traverse the data array, pre-load the pictures, calculate the height of the scaled pictures, and store them.Also since picture loading is asynchronous, count the variables and start rendering the page when the last picture loading is complete.

loadImagesHeight(list){
                var count = 0; //Used to count whether all picture heights have been obtained
                list.forEach((item, index)=>{
                    //Create picture objects, load pictures, calculate picture height
                    var img = new Image();
                    img.src = item.cover;
                    img.onload = img.onerror = (e)=>{
                        count++;
                        if (e.type == 'load'){ //Pictures loaded successfully
                            //Calculate the height of the zoomed picture: the original height of the picture/Original width = Scaled Height/Scaled Width
                            list[index].imgHeight = Math.round(img.height * this.boxWidth / img.width);
                            // console.log('index: ', index, ', load suc, imgHeiht: ', list[index].imgHeight);
                        }
                        else{ //Picture failed to load, giving a default height of 50
                            list[index].imgHeight = 50;
                            console.log("index:  ", index, ", Load error:", e);
                        }

                        //Loading finishes the last picture height and begins the next step of data processing
                        if (count == list.length){
                            this.resolveDataList(list);
                        }
                    }
                })
},

 

2.3, Render page, set absolute positioning

After all pictures get their height by preloading, start rendering the page.Then iterate through the box labels where all the pictures are located, get the height of the box, and set the absolute positioning of each box.

resolveDataList(list){ //Processing data
                //Drop-down refresh to empty the original data
                if (this.pageIndex <= 1){
                    this.itemCount = 0;
                    this.dataList = [];
                    this.lastRowHeights = [0, 0]; //Stores the last row height of each column in clear 0
                }

                if (list.length >= this.pageSize){
                    this.pageIndex++;  //Next page
                }
                else{
                    this.finished = true; //current tab All data of type has been loaded
                }

                //Merge old and new array data
                this.dataList = [...this.dataList, ...list];
                //Determine if a page has data
                this.haveData = this.dataList.length > 0 ? 2 : 1;
                this.isLoading = false; //Drop-down refresh request complete
                this.loading = false; //Drop-up Load More Requests Completed

                console.log("...datalist: ", this.dataList);
                console.log("...this.isLoading: ", this.isLoading)

                this.$nextTick(()=>{
                    setTimeout(()=>{
                        //Rendering complete, calculating each item Width and height, set label coordinate positioning
                        this.setItemElementPosition();
                        this.isLoading = false; //Drop-down refresh request complete
                        this.loading = false; //Drop-up Load More Requests Completed
                    }, 1000)
                });
            },
            //Get each item Label height, settings item Location of
            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];
                    //Column index of minimum height of previous label
                    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;

                //Modify the height of parent labels
                let maxHeight = Math.max.apply(null, this.lastRowHeights);
                parentEle.style.height = maxHeight + 'px';

                this.$toast.clear();
                console.log("...boxEles: ", boxEles.length, ", maxH: ", maxHeight);
            },

 

2.4. Other Instructions

Other pages include functions such as pull-refresh and pull-load, which are used by Supported Component Library In PullRefresh and List This set of composite components.It feels great and the steps are easy to use.Another is that when the page is rendered, there will be page flicker phenomenon, which is handled by a css animation, and the effect is much better.But there was still a slight flicker on the first load.Wait until you find a better way to update.

 

Full effect DEMO address: https://github.com/xiaotanit/tan_vue/blob/master/src/views/PageWaterFall.vue

Keywords: Javascript JSON network Vue axios

Added by tsiedsma on Mon, 13 Jan 2020 18:43:11 +0200