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:
- According to the order of loading data, add labels to show the effect in turn;
- Select which method to realize waterfall flow, and here select the absolute positioning method;
- 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.
- 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.
- 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.
- 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.