Waterfall flow must have been popular for a few years. First, it was the wave set off by Pinterest, and then domestic design mushroomed, with many examples of waterfalls popping up, such as Mogujie, Mark’s (but recently it has been involved in pornography, and it seems to have been teased), and Taobao’s “Wow” ”. These are all great examples, and today we’re going to talk about waterfall flow.
1. Absolute layout:
JS implementation principle
In fact, the main difficulty of the waterfall style is how to arrange the images neatly in the corresponding columns and when to start refreshing and loading the images.
The main logic and algorithm for neatly arranging pictures is to first obtain how many columns can be placed in the container, and then, through calculation, store the height of the first column, and then traverse the remaining heights (except for the elements in the first column) and place them respectively. Enter the column with the smallest height. Add one by one, and finally end the traversal.
The settings for starting the refresh are very simple. The waterfall refresh is only related to one event, that is, window.onscroll. The main algorithm is that when the page slides to the lowest height, it starts loading nodes and adding them. Of course, the number of nodes added is not fixed.
Let’s start with the code. I will explain it in three parts. One is the arrangement of images, the other is setting the loading position. In addition, I will add a responsive loading.
1. Picture arrangement
var $ = function() { return document.querySelectorAll.apply(document, arguments); } var arrHeight = []; //得到分列的高度 var columns = function() { //计算页面最多可以放多少列 var containerW = $("#main")[0].clientWidth, pinW = $(".pin")[0].offsetWidth; return Math.floor(containerW / pinW); } var getIndex = function(arr) { //获得最小高度的index var minHeight = Math.min.apply(null, arr); //获得最小高度 for (var i in arr) { if (arr[i] === minHeight) { return i; } } } //根据列数确定第一排img的高度并放入数组当中。 var setCenter = (function() { //通过列数设置宽度 var main = $('#main')[0]; //获得罩层 var getPadding = function() { //设置padding var col = columns(); //获得最后一列 var padding = main.clientWidth - col * $('.pin')[0].offsetWidth; return padding / 2; } var getComputedStyle = function(ele) { //兼容IE的支持情况 if (window.getComputedStyle) { return window.getComputedStyle(ele); } else { return ele.currentStyle; } } var getPinPad = function() { //获得pin的padding值 var pin = $(".pin")[0]; return parseInt(getComputedStyle(pin).paddingLeft); } return function() { //设置宽度 main.style.padding = `0 ${getPadding()}px 0 ${getPadding()-getPinPad()}px`; } })(); var overLoad = function(ele) { var index = getIndex(arrHeight), minHeight = Math.min.apply(null, arrHeight), //获取最小高度 pins = $('.pin'), style = ele.style; style.position = "absolute"; style.top = minHeight + "px"; //设置当前元素的高度 style.left = pins[index].offsetLeft + "px"; arrHeight[index] += ele.offsetHeight; } //初始化时执行函数 var init = function() { var pins = $(".pin"), col = columns(); setCenter(); //设置包裹容器的宽度 for (var i = 0, pin; pin = pins[i]; i++) { if (i < col) { //存储第一排的高度 arrHeight.push(pin.offsetHeight); } else { overLoad(pin); //将元素的位置重排 } } }
There are a total of 7 functions (large functions) and one variable. Let’s talk about the idea. First of all, the function executed after the page is onloaded is init. You must know that a js program must have its entrance. The key depends on how you find it. Then we go deep into the init function body and observe. The business logic executed in init is to store the height of the first row of elements and then rearrange the remaining elements. Use the columns function to get the maximum number of columns that can be placed in the current window, and then set the center of the container (just set it through padding). Next, traverse the pin's cell box, store the height of the first row of elements in the arrHeight array, and add the remaining The elements below are rearranged. There is no need to explain other functions. Let’s focus on the overLoad function.
2. overLoad
var overLoad = function(ele) { var index = getIndex(arrHeight), minHeight = Math.min.apply(null, arrHeight), //获取最小高度 pins = $('.pin'), style = ele.style; style.position = "absolute"; style.top = minHeight + "px"; //设置当前元素的高度 style.left = pins[index].offsetLeft + "px"; arrHeight[index] += ele.offsetHeight; }
There is a getIndex function in overLoad to get the index of the minimum height, and then you can set the position of the incoming ele element (absolute positioning). top is the px value of the minimum height in the array, and left is set for the first row. The left margin position of the Index element. Finally update the height, ok!!! that's enough.
3. Set the loading location
var dataInt = [{ 'src': '1.jpg' }, { 'src': '2.jpg' }, { 'src': '3.jpg' }, { 'src': '4.jpg' }, { 'src': '1.jpg' }, { 'src': '2.jpg' }, { 'src': '3.jpg' }, { 'src': '4.jpg' }]; function isLoad() { //是否可以进行加载 var scrollTop = document.documentElement.scrollTop || document.body.scrollTop, wholeHeight = document.documentElement.clientHeight || document.body.clientHeight, point = scrollTop + wholeHeight; //页面底部距离header的距离 var arr = $('.pin'); var lastHei = arr[arr.length - 1].getBoundingClientRect().top; return (lastHei < point) ? true : false; } //处理滑动 var dealScroll = (function() { var main = $('#main')[0], flag = true; return function() { if (isLoad() && flag) { for (var i = 0, data; data = dataInt[i++];) { var div = document.createElement('div'); div.innerHTML = temp(data.src); div.className = "pin"; main.appendChild(div); overLoad(div); //和上面的overload有耦合性质 } flag = false; setTimeout(function() { //控制滑行手速,时间越长对速度的滑动时间影响越大。 flag = true; }, 200); } } })(); function temp(src) { return ` <div class="box"> <img src="http://cued.xunlei.com/demos/publ/img/P_00${src}"/> </div> `; }
Actually, the essence is in the previous part. This is just a means of loading data. Of course, you can click to load (manual trigger), or other loading methods. Of course, how you set it up is entirely up to you. Therefore, follow the trend and still scroll down to load. Continue and find the entry function->dealScroll. The task of this function is to determine whether loading can be performed through the isload function. Let’s take a look at the isload function. This is the key point of rolling loading.
function isLoad() { //是否可以进行加载 var scrollTop = document.documentElement.scrollTop || document.body.scrollTop, wholeHeight = document.documentElement.clientHeight || document.body.clientHeight, point = scrollTop + wholeHeight; //页面底部距离header的距离 var arr = $('.pin'); var lastHei = arr[arr.length - 1].getBoundingClientRect().top; return (lastHei < point) ? true : false; }
By calculation, the position of the bottom of the page from the viewport (lower part of the toolbar) is compared with the absolute position of the last element. If the sliding distance exceeds, loading is enabled.
yeah~ That's over.
back to dealScroll
The next step is to look at the loading part. There is actually nothing to say about this part. It is to create a div node, then place it at the end of the container, and use the overLoad function to handle the position of the node. In addition, at the end of the function, I set up a trick to control the sliding speed. By throttling the function, I can prevent the request from being too slow and the user repeatedly sending requests, causing a waste of resources.
Then, this part can come to an end.
4. Responsive
The last part is responsiveness. This part is also super simple. As long as you do a good job of encapsulation, in fact, this part is just adding a resize event. Let’s continue looking for the entry function.
var resize = (function() { var flag; return function(fn) { clearTimeout(flag); flag = setTimeout(function() { //函数的节流,防止用户过度移动 fn(); console.log("ok") }, 500); } })();
Similarly, the idea of function throttling is used here. You must know that as a programmer, never think that users can’t do anything stupid. For example, when you have nothing to do, drag the browser window to play, zoom in, zoom out, and then Enlarge... In fact, I often do this, because I don’t have a girlfriend and I get tired of writing code, so I just drag the browser to play. Therefore, considering the needs of our single dog, it is very necessary to use function throttling. If you are interested in children's shoes, you can refer to my previous article to learn more. To explain, the callback function here refers to the init function, but some changes need to be made to init. See details.
var update = function(ele) { //当resize的时候,重新设置 ele.style.position = "initial"; } //初始化时执行函数 var init = function() { var pins = $(".pin"), col = columns(); arrHeight = []; //清空高度 setCenter(); //设置包裹容器的宽度 for (var i = 0, pin; pin = pins[i]; i++) { if (i < col) { //存储第一排的高度 arrHeight.push(pin.offsetHeight); update(pin); } else { overLoad(pin); //将元素的位置重排 } } }
Update needs to be added above to update the new first row of elements.
Then you can just move it over and use it.
This is definitely most of the content of the layout. For another layout method of JavaScript waterfall flow, please refer to the next article "Detailed explanation of JavaScript implementation of waterfall flow layout" .