瀑布流在上一篇已经说了我实现的思路和方法,不懂原理的小伙伴可以去看看,当然这一篇代码是继着上一篇的瀑布流来实现的,也可以直接在这里看代码,代码中的注释都齐全。
代码的实现是我手撸,但是思路是网上看各大佬的讲解来的,然后自己优化了点,加了节流和更直观的看到懒加载的情景。这里主要是为了附上完整的代码,方便各位看官能去直接ctrl+C
懒加载
概念:为了减少流量,提升网站性能,图片展示型网站会采用懒加载。一般第一屏的图片直接显示,从第二屏开始采用懒加载。(大概就是这个意思)
实现思路
① 通常的做法是在对标签的src属性放占位图片的地址,而真实的图片地址放在img标签的某个属性中,如data-img或original之类的。
就像这样:< img data-img=“真实地址” src=“占位图片地址” >
鼠标滚动时判断待加载资源到可视区域顶端的距离,如小于屏幕高度,把data-img的值赋给src,然后去掉data-img属性。
② 第一次渲染时,展现部分图片,然后监听浏览器的滚动事件,进行判断,判断之前展示的最后一张图片
的offsetTop值和浏览器视口的高度+滚动的高度
的大小,如果小于则进行后续图片接口的请求,重新进行渲染。
选择方案
我这里实现的是第二种方法,因为第一种方法就是个样例,实际需求我get不到,因为他是一次就全拿到数据,然后进行类似于css属性display:none,显示与隐藏的关系。但是在实际开发中,我们很可能需要的就是图片一次就请求部分,当看完了这部分后再次去请求,然后渲染到dom树上去。
直接上代码吧,里面有详细的注释,因为浏览器的滚动事件会触发太频繁,我这里图片懒加载后的请求渲染事件用了闭包节流
,同瀑布流的节流是一个理。为了直观的展示,高度判断里面我加了offsetHeight
,实际项目中,这个可以去掉(加不加这个的效果你们自行体会)
<!DOCTYPE html>
<br lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
}
#box{
position: relative;
}
#box .item{
position: absolute;
width: 300px;
}
#box .item img{
width: 100%;
}
</style>
</head>
<body>
<!--瀑布流实现-->
<div id="box">
<div class="item">
<img src="./image/p1.jpeg" alt="">
</div>
<div class="item">
<img src="./image/p4.jpeg" alt="">
</div>
<div class="item">
<img src="./image/p3.jpeg" alt="">
</div>
<div class="item">
<img src="./image/p2.jpeg" alt="">
</div>
<div class="item">
<img src="./image/p3.jpeg" alt="">
</div>
<div class="item">
<img src="./image/p4.jpeg" alt="">
</div>
<div class="item">
<img src="./image/p2.jpeg" alt="">
</div>
<div class="item">
<img src="./image/p1.jpeg" alt="">
</div>
</div>
<script>
//瀑布流实现
let box = document.getElementById('box');
let items = box.children;//包裹图片的div类数组
function waterImg(){
console.log(11)
let clientWidth =getClient().width;
let columnNums =parseInt(clientWidth/300); //根据网页可视宽度计算一行的列数
let leftWeight = 10;
let oneColumsHeight = [];//存储第一列图片的高度--为了计算后面列数图片的排列情况
for(let i=0;i<items.length;i++){
if(i<=columnNums-1){//这里判断是否是第一行的图片--根据列数和下标来对比
items[i].style.left=i*(300+leftWeight)+'px';
//浏览器从小到大拉伸--这里要设置
items[i].style.top = 0 + 'px';
oneColumsHeight.push(items[i].offsetHeight);//offsetWidth = width + padding + border, 和margin无关。
}else{
//先找到存储第一列高度数组里面最小的那个高度数据
let minHeight = oneColumsHeight[0];
let index = 0;
for(let j = 0;j<oneColumsHeight.length;j++){
if(minHeight>oneColumsHeight[j]){
minHeight = oneColumsHeight[j];
index = j
}
}
//找到过后给这个图片的top-left属性进行赋值(因为不是第一行了--top属性要进行赋值)
items[i].style.top = oneColumsHeight[index]+leftWeight + 'px';
items[i].style.left = items[index].offsetLeft+'px';
//然后这里--因为oneColumsHeight中最短的那个height下面已经加了一张图片了--所以这里要给最短的height的值进行增加(增加的高度为新加的那张图片的高度)
// console.log(items[i].offsetHeight)
oneColumsHeight[index] = oneColumsHeight[index]+items[i].offsetHeight + leftWeight;
}
}
}
//加载时触发
window.onload= waterImg;
//浏览器视口改变触发
window.onresize = throttle(waterImg,500);
//获取视口宽度--高度方法
function getClient() {
return {
width:window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
height:window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
}
}
//获取浏览器滚动高度的方法
function getScrollTop() {
return window.pageYOffset || document.documentElement.scrollTop;
}
//截流
function throttle(fn,wait) {
let timer;
return function () {
if (!timer){
timer = setTimeout(function () {
fn();
timer = null;
},wait);
}
}
}
//上方就是瀑布流的一个实现--现在继着上方实现一个图片的懒加载(主要是先判断上方图片的最后一张的offsetTop+offsetHeight的值在不在视口当中--然后使用了节流函数--防止过多触发)
window.onscroll=throttle(scollFunc,500);
function scollFunc() {
if(getClient().height + getScrollTop()>= (items[items.length-1].offsetTop+items[items.length-1].offsetHeight)){
//这里模拟后端传过来的图片数据(如果是真实环境--这里触发ajax接口即可)
let imagesData = ['./image/p4.jpeg','./image/p1.jpeg','./image/p2.jpeg']
for(let i = 0;i<imagesData.length;i++){
console.log(1)
let div = document.createElement('div');
div.className = 'item';
div.innerHTML=`<img src='${imagesData[i]}'>`;
box.appendChild(div);
}
waterImg();
}
}
</script>
</body>
</html>
tips:上方的代码直接复制就可运行,其中图片路径自行修改下。我这里是原生js实现的,如果是vue的实际操作中,还可以进一步优化,图片的请求渲染事件里面配合着节流事件加个加载状态的提示框或者loding状态。。