vue realizes network picture waterfall flow + pull-down refresh + pull-up load more

1, Thought analysis and effect drawing

Use vue to achieve a waterfall flow effect, load network pictures, and have more functional effects of pull-down refresh and pull-up load. Then, for the realization of these effects, draw the following ideas:

  1. According to the order of loading data, add labels to show the effect in turn;
  2. Select which method to realize waterfall flow, and here select the absolute positioning method;
  3. Key problem: because the width and height of each picture are different, and the width of all pictures is required to be the same in the waterfall flow, and the height is scaled proportionally with the width. And because the loading of pictures is asynchronous and delayed. Without knowing the height of the picture, the item box in which each picture is located is difficult to locate absolutely. Therefore, obtaining the height of all pictures before rendering the page is the key to solve the problem! Here, the Image class in js is selected to obtain the width and height of the picture in advance by preloading the picture. In addition, a temporary variable is used to calculate whether the height of all pictures has been obtained. When all the picture heights are obtained, start rendering the page.
  4. After the page is rendered, get the box where all the pictures are located, cycle to calculate the height of the box, and start setting the absolute positioning of each box item.
  5. Flickering occurs when the page is rendered. How to solve this problem? An animation style is used here. However, when it is loaded for the first time, it will still flash a little.
  6. Then there are more effects of pull-down refresh and pull-up load. Here, we use the combination components of favorite vant components, PullRefresh and List.

Static screenshot:

 

2, Specific implementation steps

2.1. Page structure design and test data preparation.

Prepare a json file data locally and put it in the public folder of the project. Note that the local test data must be placed in the public folder, and the data can only be requested when the network requests. This is vue3 x. A new axios dependency package is added to make network requests. Some screenshots and key codes:

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

         let list = res.data.data ? res.data.data: [];
         if (list.length > 0){
             //Get pageSize data from the list
             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 the preloaded picture and obtain the picture height
          }
          else {
              this.loadImagesHeight(list); 
          }
      }).catch((res)=>{
           console.log("..fail: ", res);
           this.$toast.clear();
           this.isLoading = false; //Pull down refresh request completed
           this.loading = false; //Pull up load more requests complete
     })
},

 

2.2. Preload the picture and store the picture height

After obtaining the data, traverse the data array, preload the picture, calculate the height of the picture after scaling, and store it. At the same time, because the picture loading is asynchronous, the variable count is used. When the last picture is loaded, the page will be rendered.

loadImagesHeight(list){
                var count = 0; //Used to count whether all picture heights have been obtained
                list.forEach((item, index)=>{
                    //Create a picture object, load the picture, and calculate the height of the picture
                    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 image after scaling: original height / original width = height after scaling / width after scaling
                            list[index].imgHeight = Math.round(img.height * this.boxWidth / img.width);
                            // console.log('index: ', index, ', load suc, imgHeiht: ', list[index].imgHeight);
                        }
                        else{ //Failed to load the picture. Give a default height of 50
                            list[index].imgHeight = 50;
                            console.log("index:  ", index, ", Loading error:", e);
                        }

                        //After loading the height of the last picture, start the next step of data processing
                        if (count == list.length){
                            this.resolveDataList(list);
                        }
                    }
                })
},

Computer embroidery factory http://www.szhdn.com Guangzhou brand design companyhttps://www.houdianzi.com

2.3. Render the page and set the absolute positioning

After all pictures are preloaded to obtain the picture height, start rendering the page. Then traverse the box labels where all pictures are located, obtain the box height, and set the absolute positioning of each box.

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

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

                //Merge new and old array data
                this.dataList = [...this.dataList, ...list];
                //Determine whether the page has data
                this.haveData = this.dataList.length > 0 ? 2 : 1;
                this.isLoading = false; //Pull down refresh request completed
                this.loading = false; //Pull up load more requests complete

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

                this.$nextTick(()=>{
                    setTimeout(()=>{
                        //After rendering, calculate the width and height of each item and set the label coordinate positioning
                        this.setItemElementPosition();
                        this.isLoading = false; //Pull down refresh request completed
                        this.loading = false; //Pull up load more requests complete
                    }, 1000)
                });
            },
            //Get the label height of each item and set the positioning of the 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];
                    //Column index of the minimum height of the 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 the parent label
                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, such as pull-down refresh and pull-up load more, use the combination of PullRefresh # and # List in the favorite component library. The steps are very simple and the effect is great. In addition, when the page is rendered, the page flickers. Later, a css animation is used to deal with this phenomenon, and the effect is much better. However, there is still a slight flicker during the first loading. We'll update it later when we find a better way.

Tags: Framework

Posted by lead2gold on Wed, 11 May 2022 19:08:46 +0300