滾動載入圖片(懶載入)實現原理
本文主要通過以下幾方面來說明懶載入技術的原理,個人前端小菜,有錯誤請多多指出
一、什麼是圖片滾動載入?
通俗的講就是:當訪問一個頁面的時候,先把img元素或是其他元素的背景圖片路徑替換成一張大小為1*1px圖片的路徑(這樣就只需請求一次),只有當圖片出現在瀏覽器的可視區域內時,才設定圖片正真的路徑,讓圖片顯示出來。這就是圖片懶載入。
二、為什要使用這個技術?
比如一個頁面中有很多圖片,如淘寶、京東首頁等等,如果一上來就傳送這麼多請求,頁面載入就會很漫長,如果js檔案都放在了文件的底部,恰巧頁面的頭部又依賴這個js檔案,那就不好辦了。更為要命的是:一上來就傳送百八十個請求,伺服器可能就吃不消了(又不是隻有一兩個人在訪問這個頁面)。
因此優點就很明顯了:不僅可以減輕伺服器的壓力,而且可以讓載入好的頁面更快地呈現在使用者面前(使用者體驗好)。
三、怎麼實現?
關鍵點如下:
1、頁面中的img元素,如果沒有src屬性,瀏覽器就不會發出請求去下載圖片(也就沒有請求咯,也就提高效能咯),一旦通過javascript設定了圖片路徑,瀏覽器才會送請求。有點按需分配的意思,你不想看,就不給你看,你想看了就給你看~
2、如何獲取正真的路徑,這個簡單,現在正真的路徑存在元素的“data-url”(這個名字起個自己認識好記的就行)屬性裡,要用的時候就取出來,再設定;
3、開始比較之前,先了解一些基本的知識,比如說如何獲取某個元素的尺寸大小、滾動條滾動距離及偏移位置距離;
1)螢幕可視視窗大小:對應於圖中1、2位置處
原生方法:
window.innerHeight 標準瀏覽器及IE9+
document.documentElement.clientHeight 標準瀏覽器及低版本IE標準模式
document.body.clientHeight 低版本混雜模式
jQuery方法:$(window).height()
2)瀏覽器視窗頂部與文件頂部之間的距離,也就是滾動條滾動的距離,也就是圖中3,4對應的位置。
原生方法:
window.pageYOffet IE9+及標準瀏覽器
document.documentElement.scrollTop 相容ie低版本的標準模式
document.body.scrollTop 相容混雜模式
jQuery方法:$(document).scrollTop();
3)獲取元素的尺寸:對應於圖中5,6位置處,左邊jQuery方法,右邊原生方法
$(o).width()=o.style.width;
$(o).innerWidth()=o.style.width+o.style.padding;
$(o).outerWidth()=o.offsetWidth=o.style.width+o.style.padding+o.style.border;
$(o).outerWidth(true) = o.style.width+o.style.padding+o.style.border+o.style.margin;
注意:要使用原生的style.xxx方法獲取屬性,這個元素必須已經有內嵌的樣式,如<div style="...."></div>;
4)獲取元素的位置資訊:對應圖中7.8位置處
返回元素相對於document頂部,左邊的距離
jQuery:
$(o).offset().top 元素距離文件頂部的距離
$(o).offset().left 元素距離文件左邊緣的距離
原生:getoffsetTop()
4、知道如何獲取元素尺寸、偏移距離後,接下來一個問題就是:如何判斷某個元素進入或者即將進入可視視窗區域?下面也通過一張圖來說明問題。
1)外面最大的框為實際頁面的大小,中間淺藍色的框代表父元素的大小,物件1~8代表元素位於頁面上的實際位置;以水平方向來做如下說明!
2)物件8左邊界相對於頁面左邊界的偏移距離(offsetLeft)大於父元素右邊界相對於頁面左邊界的距離,此時可判讀元素位於父元素之外;
3)物件7左邊界跨過了父元素右邊界,此時:物件7左邊界相對於頁面左邊界的偏移距離(offsetLeft)小於 父元素右邊界相對於
頁面左邊界的距離,因此物件7就進入了父元素可視區;
4)在物件6的位置處,物件6的右邊界與頁面左邊界的距離 大於 父元素左邊界與頁面左邊界的距離;
5)在物件5位置處時,物件5的右邊界與頁面左邊界的距離 小於 父元素左邊界與頁面左邊界的距離;此時,可判斷元素處於父元素可視區外;
6)因此水平方向必須買足兩個條件,才能說明元素位於父元素的可視區內;同理垂直方向也必須滿足兩個條件;具體見下文的原始碼;
waterfall.html
-
<html>
-
<head>
-
<title>瀑布流佈局</title>
-
<meta charset="gb2312"/>
-
<link type="text/css" rel="stylesheet" href="style2.css"/>
-
<script src="javascript2.js"></script>
-
</head>
-
<body>
-
<div id="main"><!--放置所有圖片的容器,設定id方便js獲取元素-->
-
<div class="box"><!--每張圖片用一個box裝載-->
-
<div class="pic"><!--在這裡放置圖片,並設定圖片的樣式-->
-
<img src="images/0.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/0.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/1.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/2.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/3.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/4.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/5.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/6.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/7.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/8.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/9.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/10.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/11.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/12.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/13.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/14.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/15.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/16.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/17.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/18.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/19.jpg"></img>
-
</div>
-
</div>
-
<div class="box">
-
<div class="pic">
-
<img src="images/20.jpg"></img>
-
</div>
-
</div>
-
</div>
-
</body>
-
</html>
javascript.js
-
window.onload=function(){
-
waterfall("main","box");
-
var imgs={//用json格式模擬從資料庫後臺抽取出的圖片陣列
-
"data":
-
[
-
{"src":"0.jpg"},
-
{"src":"1.jpg"},
-
{"src":"2.jpg"},
-
{"src":"3.jpg"},
-
{"src":"4.jpg"},
-
{"src":"5.jpg"},
-
{"src":"6.jpg"},
-
{"src":"7.jpg"},
-
{"src":"8.jpg"},
-
{"src":"9.jpg"},
-
{"src":"10.jpg"},
-
{"src":"11.jpg"},
-
{"src":"12.jpg"},
-
{"src":"13.jpg"}
-
]
-
}
-
window.onscroll=function(){
-
if(checheScrollSlide){
-
var oparent=document.getElementById("main");
-
//將資料塊渲染到當前頁面的尾部
-
for(var i=0;i<imgs.data.length;i++){
-
//建立class=box的div元素
-
var oBox=document.createElement("div");
-
oBox.className="box";
-
oparent.appendChild(oBox);
-
//建立class=pic的div元素
-
var oPic=document.createElement("div");
-
oPic.className="pic";
-
oBox.appendChild(oPic);
-
//建立圖片img
-
var oImg=document.createElement("img");
-
oImg.src="images/"+imgs.data[i].src;
-
oPic.appendChild(oImg);
-
}
-
//將載入的圖片進行瀑布流排列
-
waterfall("main","box");
-
}
-
}
-
}
-
//瀑布流原理:計算出頁面總共有多少列,從第二列起,將圖片依次放在總高度最小的那一列下面
-
function waterfall(parent,box){
-
//1 獲取所有裝載圖片的box
-
var oparent=document.getElementById(parent);
-
var oBoxs=getElementByClass(oparent,box);//傳入父元素和類名
-
//2 獲取頁面寬度
-
var docWidth=document.documentElement.clientWidth;
-
//3 獲取box寬度
-
var boxWidth=oBoxs[0].offsetWidth;
-
//4 計算出列數
-
var cols=Math.floor(docWidth/boxWidth);
-
//5 設定頁面寬度、居中顯示
-
oparent.style.cssText="width:"+cols*boxWidth+"px;margin:0px auto;";
-
//6 瀑布流排列
-
var hArr=[];//存放每一列的高度
-
for(var i=0;i<oBoxs.length;i++){
-
//6.1 設定第一列樣式
-
if(i<cols){
-
hArr.push(oBoxs[i].offsetHeight); //儲存第一列的圖片高度
-
}else{ //6.2 設定從第二列起的樣式
-
//找出高度最小的那一張圖片的是第幾張:index
-
var minH=Math.min.apply(null,hArr);
-
var index=getIndex(hArr,minH);
-
//將下一張圖片用絕對定位設定,排列在高度最小的圖片下面,並計算此時的列高
-
oBoxs[i].style.position="absolute";
-
oBoxs[i].style.top=minH+"px";
-
oBoxs[i].style.left=oBoxs[index].offsetLeft+"px";
-
hArr[index]+=oBoxs[i].offsetHeight;
-
}
-
}
-
}
-
function getElementByClass(oparent,clsname){
-
var oElements=oparent.getElementsByTagName("*");//獲取oparent下的所有子元素
-
var oBoxs=[];
-
for(var i=0;i<oElements.length;i++){
-
if(oElements[i].className==clsname)
-
oBoxs.push(oElements[i]);
-
}
-
return oBoxs;
-
}
-
function getIndex(arr,val){
-
for(var i=0;i<arr.length;i++)
-
if(arr[i]==val)
-
return i;
-
}
-
//檢測是否具備了滾動載入資料塊的條件
-
function checheScrollSlide(){
-
var oparent=document.getElementById("main");
-
var oBoxs=getElementByClass(oparent,"box");
-
//最後一個Box所在列的高度+最後一個box高度的一半
-
var lastBoxH=oBoxs[oBoxs.length-1].offsetTop+Math.floor(oBoxs[oBoxs.length-1].offsetHeight);
-
//滾動條拖動的距離(注意混雜模式document.body.scrollTop和標準模式document.documentElement.scrollTop)
-
var scrollTop=document.body.scrollTop || document.documentElement.scrollTop;
-
//瀏覽器可視視窗的高度(注意混雜模式和標準模式)
-
var height=document.body.clientHeight || document.documentElement.clientHeight;
-
return(lastBoxH<scrollTop+height)?true:false;//當滾動條下拉到圖片的時候
-
}
style.css
-
*{
-
margin:0px;
-
padding:0px;
-
}
-
#mian{
-
position:relative;
-
}
-
.box{
-
float:left;
-
padding:15px 0px 0px 15px;
-
}
-
.pic{
-
padding:10px;
-
border:1px solid #ccc;
-
border-radius:5px;
-
box-shadow:0 0 5px #ccc;
-
}
-
.pic img{
-
width:165px;
-
height:auto;
-
}