new Image():創建圖片實體與載入單張圖片
Image 建構函式
在 Image (開頭大寫) 對於 JavaScript 來說,算是個特定的元素 (HTMLImageElement),而元素可使用 new 的方式透過建構式產生一個圖片實體。
console.log(Image) 透過開發者工具,可以看到回應內容是個 function Image()。
prototype: HTMLImageElementPrototype 裡列出相的屬性與設定、還有 Image 方法等…在 Image 實體裡。
使用 new Image() 產生空的圖片實體
使用建構函式 new Image() 建立一個圖片實體出來,透過 console.log(new Image()); 會回應出 <img> 所新建的圖片實體出來。
展開後裡面就有圖片相關的屬性與方法,可以用來操作 DOM 元素上的圖片內容。
new Image() 與 document.createElement('img') 的同異之處:
二個方式產生圖片實體的結果是一樣的,只是在支援使用上 document.createElement('img') 相容性會比較好 (IE8),與 new Image() 使用效能上 document.createElement('img') 也是比較優,生成 image 次數小於十萬次時,選擇 createElement (透過同時跑 for 迴圈就可比較出結果)。
次數比較少時, createElement 的效率更高,同時,這裡還提供了一種更高效的做法,就是使用頁面中的一個固定的 img 元素,同時在改變 img 的 src 的時候,設置 visibility 為 hidden,避免觸發 reflow 提高性能。
123456789101112131415161718192021222324252627 (function () {var IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';var COUNT = 1000000;console.time('new Image');for (var i = 0; i < COUNT; i++) {var cacheImage = new Image();cacheImage.src = IMG;cacheImage.onload = function () {cacheImage.src = '';cacheImage = null;};}console.timeEnd('new Image');console.time('createElement');for (var i = 0; i < COUNT; i++) {var cacheImage1 = document.createElement('img');cacheImage1.src = IMG;cacheImage1.onload = function () {cacheImage1.src = '';cacheImage1 = null;};}console.timeEnd('createElement');})();資料來源:
– stackoverflow 英文討論:Is there a difference betweennew Image()
anddocument.createElement('img')
?
– Javascript-new Image()
和document.createElement('img')
之間有區別嗎
– new Image() 與 document.createElement(‘img’) 的異同
使用 Image() 透過 src 取得圖片檔後,在 DOM 中產生圖片結構呈現畫面
MDN 說明 Image()
Image() 函數將會創建一個新的 HTMLImageElement 實例。
它的功能等價於 document.createElement('img')
1 | Image(width, height) |
width:圖片的寬度 (即 width 屬性).
height:圖片的高度 (即 height 屬性).
如果不透過 HTML 裡的 <body></body> 內插入 <img src=""> 的方式使用的話,可以透過 JavaScript 的 new Image() 建構式方式,在記憶體中插入一個新的圖片實體出來。
如果要直接透過 JavaScript 的 new Image() 方式建立出一筆 HTML 的元素出來,將所建的新的圖片透過賦值的方式,將路徑加到圖片的 .src 屬性上,就可以直接將元素加上圖片路徑,加上 src 的同時瀏覽器就會向伺服器發送請求,要求伺服器傳送對應的圖片檔案進來瀏覽器中,主機回應後接著就會進行傳送圖片檔的動作。
1 2 3 4 5 | let image = new Image(); let srcUrl = 'https://unsplash.it/3600/1800/?random=1'; image.src = srcUrl; document.body.appendChild(image); |
See the Pen 使用 Image() 透過 src 取得圖片檔後,在 DOM 中產生圖片結構呈現畫面 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
伺服器的圖片使用 unsplash 假圖服務 ,在網址 https://unsplash.it/ 的後方加上參數 3600/1800/ (圖片尺寸大小) 與 ?random=1 (亂數取得不同圖片)。
圖片載入情形
使用 .onload 與 .onerror 事件方法了解戴入情形
產生了圖片實體後透過 .src 將圖片路徑賦值,就可發送請求向伺服器要求傳送圖片檔案傳送,在傳送過程不一定會取得到圖片,也因此可以透過事件觸發回應的內容。
.onload 事件在圖片加載完成後立即執行
.onerror 載入期間發生錯誤時,由 error 會被 error 事件跟蹤到。
這裡以 Google 的圖片 https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png 為例來操作二個事件,其中錯誤的部份在網域名稱多加 111 讓遠端圖片取不到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // let onloadImg = document.createElement('img'); let onloadImg = new Image(); onloadImg.src = 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'; // (有圖片路徑) console.log('onloadImg 創建時 -> ', onloadImg); onloadImg.onload = function () { console.log(`Image loaded, size ${onloadImg.width}x${onloadImg.height}`); document.body.appendChild(onloadImg); }; // let onerrorImg = document.createElement('img'); let onerrorImg = new Image(); console.log('onerrorImg 創建時 -> ', onerrorImg); onerrorImg.src = 'https://www.google111.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'; // (故意打錯圖片路徑於 google 名後加 111) onerrorImg.onerror = function () { console.log('Error occurred while loading image'); }; |
See the Pen 使用 Image() 的 .onload 與 .onerror 載入事件 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
以上程式碼運作的部份,當圖片載入的過程取不到圖片,就會觸發 .onerror 事件執行函式。
在開發者工具就會回應 "Error occurred while loading image" 的訊息。
.complete 判斷圖片是否載入完成 (布林值 true)
透過 .complete 的判斷式也是了解圖片載入的情形,配合著 if() else {} 分別針對該圖片做出對應的動作。
.complete 另外也可以理解成圖片是否載入成功到瀏覽器快取內,如果有圖片的話就會為 true。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // let onloadImg = document.createElement('img'); let onloadImg = new Image(); console.log("onloadImg 創建時 -> ", onloadImg); onloadImg.src = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"; // (有圖片路徑) console.log("onloadImg.complete -> ", onloadImg.complete); if (onloadImg.complete) { console.log(`Image loaded, size ${onloadImg.width} x ${onloadImg.height}`); // 確任圖片戴入完成在 DOM 元素中加入圖片 document.body.appendChild(onloadImg); } else { console.log("圖片載入狀態未確立"); } |
See the Pen 使用 Image() 的 .complete by Jimmy_Wu (@Jimmy_Wu) on CodePen.
.complete 與 .onload 的混用
針對舊瀏覽器 IE 的圖片狀態 (相容性),透過 .complete 比較好操作結結果,IE 沒有觸發 .onload 事件,而是因為 IE 中加載緩衝區的速度太快,以至於沒有運行到 .onload 的時候,圖片已經被加載完畢了。因此,可以先告訴瀏覽器如何處理這張圖片,然後再製定這張圖片的來源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function onImageLoaded(url, cb) { var image = new Image() image.src = url if (image.complete) { // 圖片已經被載入 cb(image) } else { // 如果圖片未被載入,則設定載入時的回調 image.onload = function () { cb(image) } } } onImageLoaded('https://google.com/favicon.ico', function (icon) { console.log('Google 的 Favicon 載入完成啦!') }) |
另外透過三元運算子結合 .complete 與 .onload 來使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | function addImage(srcUrl) { let dateTime = Date.now(); // 時間戳記 let image = new Image(); // 建立新的圖片 // console.log('image.complete', image.complete, image); // // complete 可返回瀏覽器是否已完成對圖像的加載 (完成 true / fasle)。 image.complete ? /* * 圖片已經被載入 * onload 事件在圖片加載完成後立即執行 */ ( image.onload = () => { document.body.appendChild(image); console.log('圖片已戴入完成') } ) : console.log('圖片未完成載入') image.src = srcUrl; } addImage(`https://unsplash.it/3600/1800/?random=1`); |
See the Pen 使用 Image() 的 .complete by Jimmy_Wu (@Jimmy_Wu) on CodePen.
一次產生多筆圖片的 .complete 與 .onload
非同步但同 src 圖片路徑的 .complete
圖片透過 .src 取得圖檔時是以非同步取得的。
這部份如果是在第一次戴入 google logo 圖片時會在使用 .complete 的判斷式下會第一次先判為 false,但如果要在短時間內將畫面重整後就會為 true。
如果要將圖片產生到動元素,需要在重新執行畫面重整,才會顯示出來。
這裡產生的時間也都為同一時間。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | function addImage(srcUrl) { function getNewTime() { let today = new Date(); let toLocaleDateString = today.toLocaleDateString(); let hour = today.getHours(); let min = today.getMinutes(); let sec = today.getSeconds(); let dateTime = `${toLocaleDateString}-${hour}:${min}:${sec}`; // let = Date.now(); // 時間戳記 return dateTime; } let image = new Image(); // 建立新的圖片 console.log("產生空 image 於 src 取資料前", image); /* 觸發取圖片資料 */ image.src = srcUrl; // 圖片執行 .src 之後 image.complete 第一時間不會為 tree,會先到 else if (image.complete) { console.log("image.complete"); document.body.appendChild(image); } else { console.log("image.complete -> else"); } console.log(`觸發 srcUrl 時間 ${getNewTime()}`); } for (let a = 1; a <= 50; a++) { addImage( `https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png` ); } |
See the Pen 使用 Image() 產生多圖 .complete – url 圖片路徑為同一張 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
非同步但不同 src 圖片路徑的 .complete 與 .onload
但如果讓圖片使用線上圖庫的 ?random= 參數,在每次執行時都會取到不同張的圖片,此時的 .complete 的判斷式永遠也都 false,無法在 .complete 下來操作圖片。
產生的時間會因為圖片戴入的前後有所不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | function addImage(srcUrl) { function getNewTime() { let today = new Date(); let toLocaleDateString = today.toLocaleDateString(); let hour = today.getHours(); let min = today.getMinutes(); let sec = today.getSeconds(); let dateTime = `${toLocaleDateString}-${hour}:${min}:${sec}`; // let = Date.now(); // 時間戳記 return dateTime; } let image = new Image(); // 建立新的圖片 console.log("產生空 image 於 src 取資料前", image); /* 觸發取圖片資料 */ image.src = srcUrl; // 圖片執行 .src 之後 image.complete 第一時間不會為 tree,會先到 else if (image.complete) { console.log("image.complete"); document.body.appendChild(image); } else { console.log("image.complete -> else"); } console.log(`觸發 srcUrl 時間 ${getNewTime()}`); /* img load */ image.onload = function () { document.body.appendChild(image); console.log( "image onload -> ", image, `image 建立完成時間 -> ${getNewTime()}` ); }; } for (let a = 1; a <= 50; a++) { addImage(`https://unsplash.it/3600/1800/?random=${a}`); } |
See the Pen 使用 Image() 產生多圖 .complete – url 圖片路徑後加 unsplash 線上圖庫參數 ?random= ,永遠為 false by Jimmy_Wu (@Jimmy_Wu) on CodePen.