rendering
Come on, look, look, it’s a beautiful world out there,
The renderings show the effect of lazy loading on the waterfall flow layout
data
Photo data source zhang Xinxu’s blog
Let’s start with our image link format
All link is http://cued.xunlei.com/demos/publ/img/P_${name}. The JPG format, we just need to change the value of the name, when the name value is less than 10, 00 format is x, such as 002, 003, When you’re greater than 10, it’s like 023.
define
Waterfall layout is a popular way of page layout. Pinterest was the first website to use this layout. The picture width is fixed and the height is automatic, which produces a kind of uneven beauty.
The principle of
The principle is very simple, mainly divided into the following steps
Define the height array and column number
2. Iterate over the elements and push any less than the number of columns directly into the array
3. If the value is greater than the number of columns, get the smallest value in the height array and define the top and left values of the elements
4. Importantly update the height array by adding the minimum height to the height of the current element
Now that I know how it works, how do I write the code? Here is an example of the Web side, as follows
let heightArr = []
let col = 2
let allBox = document.querySelectorAll('.box') // Get all boxesfor(let i in allBox){
letBoxWidth = allBox[0].offsetwidthlet boxHeight = allBox[i].offsetHeight
if(I < col){heightArr. Push (boxHeight) // Add the first row height}else{// Perform layout operationsletMinHeight = mac.min. apply(null, heightArr) // Obtain the minimum heightletMinIndex = getIndex(heightArr, minHeight) // The minimum height subscript is either 0 or 1 allBox[I].style.position ='absolute'
allBox[i].style.top = minHeight + 'px'
allBox[i].style.width = minIndex * boxWidth + 'px'HeightArr [minIndex] += boxHeight // Update latest height}} // Obtain subscript getIndex(arr, val){for(i in arr){
if(arr[i] == val) {
return i
}
}
}
Copy the code
Above is the main logic to achieve waterfall flow, here is roughly written, next we look at the small program how to achieve.
implementation
DOM can be accessed and manipulated directly in web pages, which is easy to implement, and there are many jquery plug-ins available. We know that there is no DOM in the applet, so how do we implement it? Let’s just switch gears.
Here we implement the waterfall flow layout in three ways.
CSS
Using CSS3 is the easiest way to do this, so let’s start with something simple,
Use the column-count attribute to set the number of columns
Use wX-if to determine whether to render the image to the left or right
wxml
<view class='container'>
<image src='{{item.url}}' wx:if="{{index % 2 != 0 }}" wx:for="{{list}}" mode='widthFix' wx:key="{{index}}"></image>
<image src='{{item.url}}' wx:if="{{index % 2 == 0 }}" wx:for="{{list}}" mode='widthFix' wx:key="{{index}}"></image>
</view>
Copy the code
wxss
.container{
column-count: 2; /* Set the number of columns */
column-gap:2rpx;
padding-left: 8rpx;
}
image{
width: 182px;
box-shadow: 2px 2px 4px rgba(0, 0, 4); }Copy the code
Js to obtain the next data, here is not described.
Node information
Applets can use the WXML node information API to get information about elements.
wxml
<view class="container">
<view wx:for="{{group}}" style='position:{{item.position}}; top: {{item.top}}; left:{{item.left}}; width:{{width}}rpx; ' class='box box-{{index}}' wx:key="{{index}}">
<image src='http://cued.xunlei.com/demos/publ/img/P_{{item.name}}.jpg' style=' height:{{height[index]}}px' bindload='load' data-index='{{index}}' class='image'></image>
</view>
</view>
Copy the code
wxss
.container{
position: relative;
display: flow-root;
}
.box{
float: left;
display: flex;
margin-left:5rpx;
box-shadow: 2rpx 2rpx 5rpx rgba(0,0,0,.3);
border: 1rpx solid #ccc;
box-sizing: border-box;
padding: 10px;
}
.box:nth-child(2){
margin-left: 12rpx;
}
image{
width: 100%;
}
Copy the code
js
Image links to http://cued.xunlei.com/demos/publ/img/P_${name}. JPG, only need to change the name
Let’s start with our data
// Create an array of length 30
const mockData = (a)= > {
return Array.from(Array(30).keys()).map(item= > {
if (item < 10) {
return '00' + item
} else {
return '0' + item
}
})
}
// Expand to the data we need
const createGroup = (a)= > {
let group = []
let list = mockData()
list.forEach(item= > {
group.push({ name: item, position: 'static'.top: ' '.left: ' '})})return group
}
Copy the code
Then the waterfall flow layout, the main code is as follows
This.setdata ({height: [...this.data.height, e.diail.height]}) this.showimg () // call render function},showImg() {let height = this.data.height
if(height.lenth ! = this.data.group.legth){// Make sure all images are loadedreturn
}
setTimeout(()=>{// Async wx.createsElectorQuery ().selectAll()'.box').boundingClientRect((ret) => {
let cols = 2
var group = this.data.group
var heightArr = [];
for (var i = 0; i < ret.length; i++) {
var boxHeight = height[i]
if (i < cols) {
heightArr.push(boxHeight + 25)
} else {
var minBoxHeight = Math.min.apply(null, heightArr);
var minBoxIndex = getMinBoxIndex(minBoxHeight, heightArr);
group[i].position = 'absolute'
group[i].top = `${minBoxHeight}px`
group[i].left = minBoxIndex * this.data.width / 2 + 'px'
group[i].left = minBoxIndex == 0 ? minBoxIndex * this.data.width / 2 + 'px' : minBoxIndex * this.data.width / 2 + 5 + 'px'
heightArr[minBoxIndex] += (boxHeight + 25)
}
}
this.setData({
group
})
wx.hideLoading()
}).exec()
}, 200)
}
Copy the code
You can see that the implementation logic is similar to the above, except that we are modifying the data, after all, the applets are data-driven.
Here we mainly listen to the bindLoad event of the image component to obtain the height of each image. Only after obtaining the height can we carry out the layout. Most of the time is also used to load the image. Of course we can. We use Node to wrap the data.
The backend processes the data
As mentioned above, getting the height of the image inside the applet is a thankless task. We use Node to get the height of the image and then wrap it up for the applet to use.
- Use Request for requests
- Get the height of the image using image-size
- Finally, the data is written to a file and a service provisioning interface is started
Here is the main problem encountered
The image-size Buffer must be a Buffer. The image-size Buffer must be a Buffer. The image-size Buffer must be a Buffer. Set encoding to NULL in the request
2. We have climbed 100 pictures here. How can we guarantee that we have climbed all of them? We could write it like this
Promise.all(list.map (item => getImgData(item))) // getImgData is a function that gets pictures and returns a PromiseCopy the code
3, if the request for a few times, found some pictures can not be obtained, error, what’s going on, after all, they did anti-crawling, congratulations you won the lottery, change the IP to try again (you can put the code on the server, or change wi-fi), in fact, we only need to climb once on the line, generated file also climb why ah.
The full code can be found at Github
Back to the applet, the interface returns the following data
Now you can see that each image has a height, so let’s implement waterfall layout. Wait, let’s do lazy loading of waterfall layout, learn more about lazy loading of applets.
How do you do that? There are two main steps
1. Layout the element waterfall flow
2. Create IntersectionObserver for lazy loading
Let’s start with our layout
wxml
<view class='container'>
<view class='pic pic-{{index}}' wx:for="{{list}}" style="height:{{item.height}}px; left:{{item.left}}; top:{{item.top}}; position:{{item.position}}" wx:key="{{item.index}}">
<image src='{{item.url}}' wx:if="{{item.show}}"></image>
<view class='default' wx:if="{{! item.show}}"></view>
</view>
</view>
Copy the code
We used wx-if to check whether the image was loaded or not using the show field.
Use a View component to hold the space and change the show field to display the image
js
Let’s use two for loops to do the layout first
let cols = 2
let list = this.data.list
let heightArr = [];
for(let i in list){
var boxHeight = list[i].height
if (i < cols) {
heightArr.push(boxHeight + 5)
} else {
var minBoxHeight = Math.min.apply(null, heightArr);
var minBoxIndex = getMinBoxIndex(minBoxHeight, heightArr);
list[i].position = 'absolute'
list[i].top = `${minBoxHeight}px`
list[i].left = minBoxIndex * 182 + 'px'
list[i].left = minBoxIndex == 0 ? minBoxIndex * 182 + 'px' : minBoxIndex * 182 + 4 + 'px'
heightArr[minBoxIndex] += (boxHeight + 5)
}
}
this.setData({
list
})
Copy the code
After the layout, IntersectionObserver is created to dynamically judge the display of image nodes
for (let i in list) {
wx.createIntersectionObserver().relativeToViewport({ bottom: 20 }).observe('.pic-' + i, (ret) => {
if (ret.intersectionRatio > 0) {
list[i].show = true
}
this.setData({
list
})
})
}
Copy the code
The last
We completed the waterfall flow layout of the applet in three ways, plus lazy loading based on waterfall flow. You can find that the use of CSS is the most simple, although the small program can not operate DOM, but we changed the data and change the DOM, the concept of change, small program development is still very cool.
And finally, have a great weekend, everybody.
github