瀑布流懒加载
HTML/CSS
<style> body{ margin:0; padding:0; list-style:none; } #container{ position:relative; } #container .box{ float:left; padding:5px; } #container .box-img{ padding:5px; border-radius:5px; box-shadow:0 0 5px #ccc; border:1px solid #B8B8B8; } #container img{ width:230px; height:auto; } </style> <body> <div id='container'> <div class='box'> <div class='box-img'> <img src="images/594A.png" /> </div> </div> <div class='box'> <div class='box-img'> <img src="images/5.jpg"> </div> </div> <div class='box'> <div class='box-img'> <img src="images/mn3.jpg"> </div> </div> <div class='box'> <div class='box-img'> <img src="images/ym.jpg"> </div> </div> <div class='box'> <div class='box-img'> <img src="images/600A.png"> </div> </div> <div class='box'> <div class='box-img'> <img src="images/605A.png"> </div> </div> </div> </body>
效果
1.0-->首先使用JS计算出每一行显示多少列的图片
列数 = 内容可视区的宽度(浏览器宽度) / 图片容器的宽度
让图片居中
var oParent = document.getElementById('container');//1.获取父元素 window.addEventListener('load',()=>{ imgLocation('box'); }); const imgLocation = (child) =>{ const oContent = getChild(child); const imgWidth = oContent[0].offsetWidth;//图片宽度 const num = ~~(document.documentElement.clientWidth / imgWidth);//每一行图片显示的列数 oParent.style.cssText = 'width:'+imgWidth*num+'px; margin:0 auto';//设置container宽度,并在浏览器中居中显示 } const getChild = (child) =>{ const childArr = []; const tagsAll = oParent.getElementsByTagName('*');//获取所有子元素 [].map.call(tagsAll,(current)=>{ if(current.className == child){ childArr.push(current); } });
提示:const是ES6的声明关键字,类似于ES5中的var声明一样.
const imgLocation = (child) =>{}这是ES6声明方法的语法; ES5原来的语法 var imgLocation = function(child){}
const num = ~~(document.documentElement.clientWidth / imgWidth)取整; 这里也可以这样写,使用Math.floor方法
const num = Math.floor(document.documentElement.clientWidth / imgWidth);
~~与Math.floor区别
~~只是单纯的去除小数,不管正负都不会改变整数部分
2.0-->根据每一列最小的高度
-->把图片放在每一列高度最小高度图片的下方
2.1-->计算每一列最小高度
前5个元素不需要操作
var oParent = document.getElementById('container');//1.获取父元素 window.addEventListener('load',()=>{ imgLocation('box'); }); const imgLocation = (child) =>{ const oContent = getChild(child);//获取所有子元素 const imgWidth = oContent[0].offsetWidth;//图片宽度 const num = ~~(document.documentElement.clientWidth / imgWidth);//每一行图片显示的列数 oParent.style.cssText = 'width:'+imgWidth*num+'px; margin:0 auto';//设置container宽度,并在浏览器中居中显示 const heightArr = []; [].map.call(oContent,(current,index) =>{ if(index < 5){ heightArr.push(current.offsetHeight); }else{ const minHeight = Math.min(...heightArr); const minIndex = getMinIndex(heightArr).index;//最小序列号 current.style.position = 'absolute'; current.style.top = minHeight + 'px'; current.style.left = oContent[minIndex].offsetLeft+'px'; heightArr[minIndex] = heightArr[minIndex] + current.offsetHeight; } }); } const getChild = (child) =>{ const childArr = []; const tagsAll = oParent.getElementsByTagName('*');//获取所有子元素 [].map.call(tagsAll,(current)=>{ if(current.className == child){ childArr.push(current); } }); function getMinIndex(arr){ return arr.reduce((a,b,index,arr) =>{ if(b <= a.val){ a.val = b; a.index = index; } return a; },{val:arr[0],index:0}); }
先是拿到第一行集合中的每一个图片高度,创建一个空的数组heightArr用于存储第一行所有图片的高度
[].map.call()也可以写成Array.prototype.map.call();在这里就直接简写[]代表空数组
[].map.call(oContent,(current,index) =>{})------>oContent是获取所有子元素的集合不是数组,所以这里需要call方法,call方法我是这样理解的,当前对象(oContent)指代这个空数组
传入两个参数(current,index) =>{}这里需要做个判断if,因为前面5列是不需要操作,但是需要一个初始值current.offsetHeight表示当前(第一行的5张图片)元素的高度.index<5是做这个
操作current.offsetHeight,因为5张图片的下标是0-4,所以只做初始化,而需要操作的是第6张图片
const minHeight=Math.min(...heightArr)表示拿到当前5张图片中最小的高度的图片 ...扩展运算符属于ES6方法 var arr = [1,2,3]; console.log(...arr);结果为1,2,3
getIndex方法 返回的是arr.reduce(a,b,index,arr)=>{...},{val:arr[0],index:0});的对象
arr.reduce(a,b,index,arr)说明这4个参数表示什么?
a表示数组中第一个值,
b表示数组中第二个值,(这个是用来和第一个值做比较的)
index表示数组中的下标
arr表示当前数组(在这里表示的是heightArr这个数组)
{val:arr[0],index:0}表示初始化对象类似--->var obj = {val:heightArr[0],index:0} obj对象有2个属性val和index,val的值是heightArr[0],index下标为0.这是不懂可以去看下js对象写法
if(b <=a.val){ a.val =b; a.index=index} 这里表示取最小值和最小值的下标.
a.val和a.index表示调用这个对象的属性---> var obj = {val:'1',index:'0'},获取对象里的值:对象.属性--->console.log(obj.val);结果为1
current.style.position,
current.style.top,
current.style.left
这三个操作表示给第6张图片定位在上一行中高度最小值图片的下方.
heightArr[minIndex] = heightArr[minIndex] + current.offsetHeight;最后操作是更新现在图片的高度(原来图片的高度+定位图片的高度=现在图片的高度)
提示:current,index是map方法里的参数 map有3个current表示当前值,index是下标 arr表示数组
3.0-->判断滚动条滚动到底部加载图片
滚动高度+可视区高度 > 加载最后一张图片距离浏览器顶部高度
window.addEventListener('load',() => {//事件监听 imgLocation('box'); const imgData = [{'src':'596A.png'},{'src':'607A.png'},{'src':'608A.png'},{'src':'609A.png'},{'src':'611A.png'}, {'src':'614A.png'},{'src':'616A.png'},{'src':'617A.png'},{'src':'618A.png'},{'src':'619A.png'},{'src':'620A.png'},{'src':'622A.png'},{'src':'637A.png'},{'src':'639A.png'},{'src':'636A.png'}]; this.addEventListener('scroll',()=>{ if(checkLoading('box')){ imgData.map((current)=>{ console.log(current); const oDiv = document.createElement('div'); oDiv.className = 'box'; oParent.appendChild(oDiv); const oImg = document.createElement('div') oImg.className = 'box-img'; oDiv.appendChild(oImg); const img = new Image(); img.src = 'images/'+current.src+''; oImg.appendChild(img); }); imgLocation('box'); } }); }); const checkLoading = (child) =>{ const oContent = getChilds(child); const lastTop = oContent[oContent.length-1].offsetTop;//最后一个图片距离浏览器的高度 const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;//滚动条高度 const pageHeight = document.documentElement.clientHeight || document.body.clientHeight;//可视区高度 if(scrollTop+pageHeight > lastTop){ return true; } }
checkLoading方法主要操作滚动条滚动到是什么位置需要加载图片
滚动高度+可视区高度 >最后一张图片距离到浏览器顶部距离
if(scrollTop+pageHeight > lastTop)返回一个true
在监听器里面操作滚动事件,addEventListener('srcoll',()=>{})动态添加div节点,最后更新所有父元素下box节点imgLocation('box');
完整代码:
<!DOCTYPE html> <html> <head> <title>瀑布流懒加载</title> <meta charset="utf-8"> </head> <style> body{ margin:0; padding:0; list-style:none; } #container{ position:relative; } #container .box{ float:left; padding:5px; } #container .box-img{ padding:5px; border-radius:5px; box-shadow:0 0 5px #ccc; border:1px solid #B8B8B8; } #container img{ width:230px; height:auto; } </style> <body> <div id='container'> <div class='box'> <div class='box-img'> <img src="images/594A.png" /> </div> </div> <div class='box'> <div class='box-img'> <img src="images/5.jpg"> </div> </div> <div class='box'> <div class='box-img'> <img src="images/mn3.jpg"> </div> </div> <div class='box'> <div class='box-img'> <img src="images/ym.jpg"> </div> </div> <div class='box'> <div class='box-img'> <img src="images/600A.png"> </div> </div> <div class='box'> <div class='box-img'> <img src="images/605A.png"> </div> </div> <div class='box'> <div class='box-img'> <img src="images/595A.png"> </div> </div> <div class='box'> <div class='box-img'> <img src="images/599A.png"> </div> </div> </div> <script> /* 1.0-->实现图片的列数和浏览器宽度关联 内容显示区宽度/图片容器的宽度 = 列数 1.1-->图片居中 设置margin 2.0-->根据每一列最小的高度 -->把图片放在每一列高度最小高度图片的下方 2.1-->计算每一列最小高度 前5个元素不需要操作 3.0-->判断滚动条滚动到底部加载图片 滚动高度+可视区高度 > 加载最后一张图片距离浏览器顶部高度 */ var oParent = document.getElementById('container');//父元素 window.addEventListener('load',() => {//事件监听 imgLocation('box'); const imgData = [{'src':'596A.png'},{'src':'607A.png'},{'src':'608A.png'},{'src':'609A.png'},{'src':'611A.png'}, {'src':'614A.png'},{'src':'616A.png'},{'src':'617A.png'},{'src':'618A.png'},{'src':'619A.png'},{'src':'620A.png'},{'src':'622A.png'},{'src':'637A.png'},{'src':'639A.png'},{'src':'636A.png'}]; this.addEventListener('scroll',()=>{ if(checkLoading('box')){ imgData.map((current)=>{ console.log(current); const oDiv = document.createElement('div'); oDiv.className = 'box'; oParent.appendChild(oDiv); const oImg = document.createElement('div') oImg.className = 'box-img'; oDiv.appendChild(oImg); const img = new Image(); img.src = 'images/'+current.src+''; oImg.appendChild(img); }); imgLocation('box'); } }); }); const checkLoading = (child) =>{ const oContent = getChilds(child); const lastTop = oContent[oContent.length-1].offsetTop;//最后一个图片距离浏览器的高度 const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;//滚动条高度 const pageHeight = document.documentElement.clientHeight || document.body.clientHeight;//可视区高度 if(scrollTop+pageHeight > lastTop){ return true; } } // function imgLocation(){}ES5写法 ()=>{}等于function() const imgLocation = (child) => {//把父元素所有符合条件的子元素取出来 const oContent = getChilds(child); const imgWidth = oContent[0].offsetWidth;//图片宽度 //const num = Math.floor(document.documentElement.clientWidth / imgWidth) //列数,这里结果有小数,取整Math.floor()向下取整 const num = ~~(document.documentElement.clientWidth / imgWidth);//第二个取整方法使用~~ oParent.style.cssText = 'width:' + imgWidth*num + 'px; margin: 0 auto'; //使用cssText设置父元素样式 //计算图片高度 const heightArr = []; [].map.call(oContent,(current,index) =>{ if(index < 5){ heightArr.push(current.offsetHeight); }else{ // const minHeight = getMin(heightArr); //const minHeight = Math.min.apply(Math,heightArr);//使用Math.min()方法获取最小值 //获取最小高度 const minHeight = Math.min(...heightArr);//扩展符... //得到最小高度序列号 // const minIndex = getMinIndex(minHeight,heightArr); const minIndex = getMinIndex(heightArr).index;//最小序列号 current.style.position = 'absolute'; current.style.top = minHeight + 'px'; current.style.left = oContent[minIndex].offsetLeft +'px'; heightArr[minIndex] = heightArr[minIndex] + current.offsetHeight;//更新最小高度 = 已求出最小高度+当前最小高度 // console.log(minIndex); } }); // console.log(num); } const getChilds = (child) => {//3.封装父元素下所有的子元素方法 const childArr = []; const tagsAll = oParent.getElementsByTagName('*');//获取所有子元素 [].map.call(tagsAll,(current) =>{//因为tagsAll是html集合不能直接使用,必须使用call方法传一个参数tagsAll,当前对象指代[](空数组) if(current.className == child){ childArr.push(current); } }); return childArr; // console.log(tagsAll); } /* function getMin(arr){//冒泡排序获取最小参数 var arrLength = arr.length; for(var i =0,ret = arr[0]; i <arrLength; i++){ ret = Math.min(ret,arr[i]); } return ret; }*/ /*function getMinIndex(minHeight,heightArr){//第一种方法 for( var i in heightArr){ if(heightArr[i] == minHeight){ return i; } } }*/ function getMinIndex(arr){ return arr.reduce((a,b,index,arr) =>{ if(b <= a.val){ a.val = b; a.index = index; } return a; },{val:arr[0],index:0}); } /*var arr = [1,2,3]; var obj = arr.reduce((a,b,index,arr) =>{//用求和方式获取当前对象下标 if(b<=a.val){ a.val = b; a.index = index; } return a; },{val:arr[0],index:0});*/ </script> </body> </html>
写得有些乱,因为既有es5写法也有es6写法