加載圖片是提高用戶體驗的一個很好方法。圖片預先加載到瀏覽器中,訪問者便可順利地在你的網站上沖浪,并享受到極快的加載速度。
這對圖片畫廊及圖片占據很大比例的網站來說十分有利,它保證了圖片快速、無縫地發布,也可幫助用戶在瀏覽你網站內容時獲得更好的用戶體驗。本文將分享三個不同的預加載技術,來增強網站的性能與可用性。
方法一:用css和JavaScript實現預加載
實現預加載圖片有很多方法,包括使用css、JavaScript及兩者的各種組合。這些技術可根據不同設計場景設計出相應的解決方案,十分高效。
單純使用CSS,可容易、高效地預加載圖片,代碼如下:
#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }
#preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }
#preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }
將這三個ID選擇器應用到(X)html元素中,我們便可通過CSS的background屬性將圖片預加載到屏幕外的背景上。
只要這些圖片的路徑保持不變,當它們在Web頁面的其他地方被調用時,瀏覽器就會在渲染過程中使用預加載(緩存)的圖片。簡單、高效,不需要任何JavaScript。
該方法雖然高效,但仍有改進余地。使用該法加載的圖片會同頁面的其他內容一起加載,增加了頁面的整體加載時間。
為了解決這個問題,我們增加了一些JavaScript代碼,來推遲預加載的時間,直到頁面加載完畢。代碼如下:
function preloader() {
if (document.getElementById) {
document.getElementById("preload-01").style.background="url(http://domain.tld/image-01.png) no-repeat -9999px -9999px";
document.getElementById("preload-02").style.background="url(http://domain.tld/image-02.png) no-repeat -9999px -9999px";
document.getElementById("preload-03").style.background="url(http://domain.tld/image-03.png) no-repeat -9999px -9999px";
}
}
function addLoadEvent(func) {
var oldonload=window.onload;
if (typeof window.onload !='function') {
window.onload=func;
} else {
window.onload=function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
在該腳本的第一部分,我們獲取使用類選擇器的元素,并為其設置了background屬性,以預加載不同的圖片。
該腳本的第二部分,我們使用addLoadEvent()函數來延遲preloader()函數的加載時間,直到頁面加載完畢。
如果JavaScript無法在用戶的瀏覽器中正常運行,會發生什么?很簡單,圖片不會被預加載,當頁面調用圖片時,正常顯示即可。
上述方法有時確實很高效,但我們逐漸發現它在實際實現過程中會耗費太多時間。相反,我更喜歡使用純JavaScript來實現圖片的預加載。
下面將提供兩種這樣的預加載方法,它們可以很漂亮地工作于所有現代瀏覽器之上。
JavaScript代碼段1
只需簡單編輯、加載所需要圖片的路徑與名稱即可,很容易實現:
<div class="hidden">
<script type="text/javascript">
var images=new Array()
function preload() {
for (i=0; i < preload.arguments.length; i++) {
images[i]=new Image()
images[i].src=preload.arguments[i]
}
}
preload(
"http://domain.tld/gallery/image-001.jpg",
"http://domain.tld/gallery/image-002.jpg",
"http://domain.tld/gallery/image-003.jpg"
)
</script>
</div>
該方法尤其適用預加載大量的圖片。我的畫廊網站使用該技術,預加載圖片數量達50多張。將該腳本應用到登錄頁面,只要用戶輸入登錄帳號,大部分畫廊圖片將被預加載。
JavaScript代碼段2
該方法與上面的方法類似,也可以預加載任意數量的圖片。將下面的腳本添加入任何Web頁中,根據程序指令進行編輯即可。
<div class="hidden">
<script type="text/javascript">
if (document.images) {
img1=new Image();
img2=new Image();
img3=new Image();
img1.src="http://domain.tld/path/to/image-001.gif";
img2.src="http://domain.tld/path/to/image-002.gif";
img3.src="http://domain.tld/path/to/image-003.gif";
}
</script>
</div>
正如所看見,每加載一個圖片都需要創建一個變量,如“img1=new Image();”,及圖片源地址聲明,如“img3.src=“../path/to/image-003.gif”;”。參考該模式,你可根據需要加載任意多的圖片。
我們又對該方法進行了改進。將該腳本封裝入一個函數中,并使用 addLoadEvent(),延遲預加載時間,直到頁面加載完畢。
function preloader() {
if (document.images) {
var img1=new Image();
var img2=new Image();
var img3=new Image();
img1.src="http://domain.tld/path/to/image-001.gif";
img2.src="http://domain.tld/path/to/image-002.gif";
img3.src="http://domain.tld/path/to/image-003.gif";
}
}
function addLoadEvent(func) {
var oldonload=window.onload;
if (typeof window.onload !='function') {
window.onload=func;
} else {
window.onload=function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
上面所給出的方法似乎不夠酷,那現在來看一個使用Ajax實現圖片預加載的方法。該方法利用DOM,不僅僅預加載圖片,還會預加載CSS、JavaScript等相關的東西。使用Ajax,比直接使用JavaScript,優越之處在于JavaScript和CSS的加載不會影響到當前頁面。該方法簡潔、高效。
window.onload=function() {
setTimeout(function() {
// XHR to request a js and a CSS
var xhr=new XMLHttpRequest();
xhr.open('GET', 'http://domain.tld/preload.js');
xhr.send('');
xhr=new XMLHttpRequest();
xhr.open('GET', 'http://domain.tld/preload.css');
xhr.send('');
// preload image
new Image().src="http://domain.tld/preload.png";
}, 1000);
};
上面代碼預加載了“preload.js”、“preload.css”和“preload.png”。1000毫秒的超時是為了防止腳本掛起,而導致正常頁面出現功能問題。
下面,我們看看如何用JavaScript來實現該加載過程:
window.onload=function() {
setTimeout(function() {
// reference to <head>
var head=document.getElementsByTagName('head')[0];
// a new CSS
var css=document.createElement('link');
css.type="text/css";
css.rel="stylesheet";
css.href="http://domain.tld/preload.css";
// a new JS
var js=document.createElement("script");
js.type="text/javascript";
js.src="http://domain.tld/preload.js";
// preload JS and CSS
head.appendChild(css);
head.appendChild(js);
// preload image
new Image().src="http://domain.tld/preload.png";
}, 1000);
};
這里,我們通過DOM創建三個元素來實現三個文件的預加載。正如上面提到的那樣,使用Ajax,加載文件不會應用到加載頁面上。從這點上看,Ajax方法優越于JavaScript。
- End -
懶加載其實就是延遲加載,是一種對網頁性能優化可方式,比如當訪問一個頁面的時候,優先顯示可視區域的圖片而不一次性加載所有圖片,當需要顯示的時候再發送圖片請求,避免打開網頁時加載過多資源。
當頁面中需要一次性載入很多圖片的時候,往往都是需要用懶加載的。
我們都知道HTML中的 <img>標簽是代表文檔中的一個圖像。。說了個廢話。。
<img>標簽有一個屬性是 src,用來表示圖像的URL,當這個屬性的值不為空時,瀏覽器就會根據這個值發送請求。如果沒有 src屬性,就不會發送請求。
嗯?貌似這點可以利用一下?
我先不設置 src,需要的時候再設置?
nice,就是這樣。
我們先不給 <img>設置 src,把圖片真正的URL放在另一個屬性 data-src中,在需要的時候也就是圖片進入可視區域的之前,將URL取出放到 src中。
<div class="container">
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img1.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img2.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img3.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img4.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img5.png">
</div>
</div>
仔細觀察一下, <img>標簽此時是沒有 src屬性的,只有 alt和 data-src屬性。
alt 屬性是一個必需的屬性,它規定在圖像無法顯示時的替代文本。 data-* 全局屬性:構成一類名稱為自定義數據屬性的屬性,可以通過 HTMLElement.dataset來訪問。
方法一
網上看到好多這種方法,稍微記錄一下。
然后判斷②-③<①是否成立,如果成立,元素就在可視區域內。
方法二(推薦)
通過 getBoundingClientRect()方法來獲取元素的大小以及位置,MDN上是這樣描述的:
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
這個方法返回一個名為 ClientRect的 DOMRect對象,包含了 top、 right、 botton、 left、 width、 height這些值。
MDN上有這樣一張圖:
可以看出返回的元素位置是相對于左上角而言的,而不是邊距。
我們思考一下,什么情況下圖片進入可視區域。
假設 constbound=el.getBoundingClientRect();來表示圖片到可視區域頂部距離; 并設 constclientHeight=window.innerHeight;來表示可視區域的高度。
隨著滾動條的向下滾動, bound.top會越來越小,也就是圖片到可視區域頂部的距離越來越小,當 bound.top===clientHeight時,圖片的上沿應該是位于可視區域下沿的位置的臨界點,再滾動一點點,圖片就會進入可視區域。
也就是說,在 bound.top<=clientHeight時,圖片是在可視區域內的。
我們這樣判斷:
function isInSight(el) {
const bound=el.getBoundingClientRect();
const clientHeight=window.innerHeight;
//如果只考慮向下滾動加載
//const clientWidth=window.innerWeight;
return bound.top <=clientHeight + 100;
}
這里有個+100是為了提前加載。
頁面打開時需要對所有圖片進行檢查,是否在可視區域內,如果是就加載。
function checkImgs() {
const imgs=document.querySelectorAll('.my-photo');
Array.from(imgs).forEach(el=> {
if (isInSight(el)) {
loadImg(el);
}
})
}
function loadImg(el) {
if (!el.src) {
const source=el.dataset.src;
el.src=source;
}
}
這里應該是有一個優化的地方,設一個標識符標識已經加載圖片的index,當滾動條滾動時就不需要遍歷所有的圖片,只需要遍歷未加載的圖片即可。
在類似于滾動條滾動等頻繁的DOM操作時,總會提到“函數節流、函數去抖”。
所謂的函數節流,也就是讓一個函數不要執行的太頻繁,減少一些過快的調用來節流。
基本步驟:
function throttle(fn, mustRun=500) {
const timer=null;
let previous=null;
return function() {
const now=new Date();
const context=this;
const args=arguments;
if (!previous){
previous=now;
}
const remaining=now - previous;
if (mustRun && remaining >=mustRun) {
fn.apply(context, args);
previous=now;
}
}
}
這里的 mustRun就是調用函數的時間間隔,無論多么頻繁的調用 fn,只有 remaining>=mustRun時 fn才能被執行。
可以看出此時僅僅是加載了img1和img2,其它的img都沒發送請求,看看此時的瀏覽器
第一張圖片是完整的呈現了,第二張圖片剛進入可視區域,后面的就看不到了~
當我向下滾動,此時瀏覽器是這樣
此時第二張圖片完全顯示了,而第三張圖片顯示了一點點,這時候我們看看請求情況
img3的請求發出來,而后面的請求還是沒發出~
當滾動條滾到最底下時,全部請求都應該是發出的,如圖
方法三 IntersectionObserver
經大佬提醒,發現了這個方法
先附上鏈接:
jjc大大:
https://github.com/justjavac/the-front-end-knowledge-you-may-dont-know/issues/10
阮一峰大大:
http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
API Sketch for Intersection Observers:
https://github.com/WICG/IntersectionObserver
IntersectionObserver可以自動觀察元素是否在視口內。
var io=new IntersectionObserver(callback, option);
// 開始觀察
io.observe(document.getElementById('example'));
// 停止觀察
io.unobserve(element);
// 關閉觀察器
io.disconnect();
callback的參數是一個數組,每個數組都是一個 IntersectionObserverEntry對象,包括以下屬性:
屬性描述time可見性發生變化的時間,單位為毫秒rootBounds與getBoundingClientRect()方法的返回值一樣boundingClientRect目標元素的矩形區域的信息intersectionRect目標元素與視口(或根元素)的交叉區域的信息intersectionRatio目標元素的可見比例,即intersectionRect占boundingClientRect的比例,完全可見時為1,完全不可見時小于等于0target被觀察的目標元素,是一個 DOM 節點對象
我們需要用到 intersectionRatio來判斷是否在可視區域內,當 intersectionRatio>0&&intersectionRatio<=1即在可視區域內。
代碼
function checkImgs() {
const imgs=Array.from(document.querySelectorAll(".my-photo"));
imgs.forEach(item=> io.observe(item));
}
function loadImg(el) {
if (!el.src) {
const source=el.dataset.src;
el.src=source;
}
}
const io=new IntersectionObserver(ioes=> {
ioes.forEach(ioe=> {
const el=ioe.target;
const intersectionRatio=ioe.intersectionRatio;
if (intersectionRatio > 0 && intersectionRatio <=1) {
loadImg(el);
}
el.onload=el.onerror=()=> io.unobserve(el);
});
});
源自:segmentfault
聲明:文章著作權歸作者所有,如有侵權,請聯系小編刪除。
JavaScript中,您可以使用HTML5的<input type="file">元素來實現圖片上傳功能。
以下是一個簡單的示例代碼,演示如何在JavaScript中上傳圖片:
HTML部分:
<input type="file" id="uploadInput">
<button onclick="uploadImage()">上傳圖片</button>
JavaScript部分:
function uploadImage() {
var fileInput=document.getElementById('uploadInput');
var file=fileInput.files[0];
if (file) {
var formData=new FormData();
formData.append('image', file);
// 發送圖片數據到服務器
// 這里可以使用XMLHttpRequest或fetch等方法發送請求
// 請根據您的需求選擇適當的方法
// 示例中使用XMLHttpRequest發送POST請求
var xhr=new XMLHttpRequest();
xhr.open('POST', '/upload', true);
xhr.onload=function() {
if (xhr.status===200) {
// 上傳成功
console.log('圖片上傳成功');
} else {
// 上傳失敗
console.log('圖片上傳失敗');
}
};
xhr.send(formData);
}
}
API部分:
[HttpPost]
[RequestSizeLimit(5242880)]
public async Task<APIResult> upload(IFormCollection collection)
{
APIResult rtn=new APIResult();
if (collection==null)
{
rtn.code=-100;
rtn.msg="圖片列表為空";
return rtn;
}
else
{
try
{
string file_path="";
// 預處理 用戶參數:用戶指定子路徑
string userPath=DateTime.Now.ToString("yyyy-MM-dd");
if (collection.ContainsKey("path"))
{
collection.TryGetValue("path", out Microsoft.Extensions.Primitives.StringValues val);
if (!val.Equals("undefined"))
{
userPath=val.ToString();
}
}
// 預處理 文件路徑
// 注意:這里可能會根據不同的環境來 修改 路徑前面是否需要添加 /
// 當發現上傳不成功,目錄無法創建時,可以嘗試修改這里
file_path=$"upload/imgs/{userPath}/";
var uploadPath=Path.Combine(_webHostEnvironment.WebRootPath, file_path);
if (!Directory.Exists(uploadPath))
{
Directory.CreateDirectory(uploadPath);
}
// 處理文件
FormFileCollection filelist=(FormFileCollection)collection.Files;
foreach (IFormFile file in filelist)
{
// 保存文件到磁盤
string name=file.FileName;
string FilePath=Path.Combine(uploadPath, name);
string type=Path.GetExtension(name);
using (var stream=System.IO.File.Create(FilePath))
{
await file.CopyToAsync(stream);
};
// 保存文件信息到表
Sys_File f=new Sys_File();
f.code="image";
f.name=name;
f.file_type=type.Trim('.');
f.file_group=userPath;
f.file_path=$"/{file_path}{name}";
f.is_active=true;
f.memo="";
f.createTime=DateTime.Now;
using (var dbctx=DBHelper.db)
{
await dbctx.AddAsync(f);
await dbctx.SaveChangesAsync();
};
// 返回消息,包含文件路徑
rtn.datas=$"/{file_path}{name}";
rtn.code=100;
rtn.msg="文件已保存!";
}
}
catch (Exception ex)
{
rtn.code=-200;
rtn.msg="圖片保存失敗!";
Log4NetUnit.Instance.Log.Error("圖片保存失敗:" + ex.Message);
}
return rtn;
}
}
在這個示例中,我們首先在HTML中創建了一個<input type="file">元素,用于選擇要上傳的圖片。
然后,我們在JavaScript中編寫了一個uploadImage函數,該函數在點擊"上傳圖片"按鈕時觸發。
在uploadImage函數中,我們首先獲取到<input>元素,并從中獲取到用戶選擇的圖片文件。
然后,我們創建一個FormData對象,并將圖片文件添加到其中。
接下來,我們可以使用XMLHttpRequest或fetch等方法將圖片數據發送到服務器。
在示例中,我們使用XMLHttpRequest發送了一個POST請求,將圖片數據作為FormData發送到/upload端點。
您需要根據您的實際情況修改URL和請求方法。
當請求完成時,我們可以根據響應的狀態碼來判斷上傳是否成功。
在示例中,如果狀態碼為200,則表示上傳成功,否則表示上傳失敗。
請注意,由于安全性限制,JavaScript無法直接訪問用戶的文件系統。
因此,用戶必須手動選擇要上傳的文件。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。