Change before you have to.

JavaScript-proxy-pattern-1

2017-07-27

JavaScript 代理模式

以瀏覽器載入圖片為例子,當圖片完全載入後才會出現在螢幕上,但是等待載入的這段時間會使得原本該出現圖片的地方為空白的狀態,一旦圖片載入完成出現時,會造成畫面元件跳動的情況。解決的方式有以下兩個方式: 1. 使用 loading 圖示顯示正在載入,圖片載入完成後取代 loading 圖示。2. 使用同等比例的小圖片當成佔位符,圖片載入完成後取代此佔位符。

1
2
3
4
// normal situation
const myImage = new Image();
myImage.src = 'https://source.unsplash.com/2560x1600/?nature,water'
document.body.appendChild(myImage);

上述為一般圖片載入情況,在圖片載入完成後,會發現畫面元件跳動的情況。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// image proxy pattern(failed)
const createImg = function(src) {
const myImage = new Image()
myImage.src = src
document.body.appendChild(myImage)
}
const proxyImg = (function() {
const realImg = new Image()
realImg.onload = function() {
createImg(this.src)
}
return function(src) {
createImg('https://m.popkey.co/163fce/Llgbv_s-200x150.gif')
realImg.src = src
}
})()
proxyImg('https://source.unsplash.com/2560x1600/?nature,water')

上述為我在撰寫階段犯的一個錯誤,我們最後需要的是圖片載入完成後,將 myImagesrcgif url 替換成 image url
問題在於 createImg 每次執行都會產生一個新的 image 物件,因此儘管 proxyImg 幫我們完成置換 url 的目的,但是最後會生成兩張圖片,不符合預期結果。


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
// image proxy pattern(loading icon)
const createImg = (function(src) {
const myImage = new Image()
document.body.appendChild(myImage)
return {
setSrc(src){
myImage.src =src
}
}
})()
const proxyImg = (function() {
const realImg = new Image()
realImg.onload = function() {
createImg.setSrc(this.src)
}
return {
setSrc(src) {
createImg.setSrc('https://m.popkey.co/163fce/Llgbv_s-200x150.gif')
realImg.src = src
}
}
})()
proxyImg.setSrc('https://source.unsplash.com/2560x1600/?nature,water')

上述為使用 loading 圖示作為代理的正確例子。由於上個例子 createImg 每次都會創立新的 Image 物件,因此這裡使用 IIFE 確保只會有一個 Image 物件產生,並釋出 setSrc 的介面供外界設定圖片的 url
proxyImg 也做了一些修改,為了確保與 createImg 介面一致,因此將介面修改成具有setSrc 的方法供外界設定圖片的 url
代理的重點在於先讓圖片執行載入(const realImg = new Image(); realImg.src = src),直到圖片載入完成後才將 url 替換掉(realImg.onload = function() {createImg.setSrc(this.src)})。


1
2
3
img {
width: 2560px
}
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
// image proxy pattern(image placeholder)
const createImg = (function(src) {
const myImage = new Image()
document.body.appendChild(myImage)
return {
setSrc(src){
myImage.src =src
}
}
})()
const proxyImg = (function() {
const realImg = new Image()
realImg.onload = function() {
createImg.setSrc(this.src)
}
return {
setSrc(src) {
createImg.setSrc('https://placem.at/places?w=80&h=50&txt=loading&overlay_color=eee')
realImg.src = src
}
}
})()
proxyImg.setSrc('https://source.unsplash.com/2560x1600/?nature,water')

上述為使用站位符 (placeholder) 作為代理的正確例子。與上個例子稍有不同的地方在於代理圖片與原圖片等比例,但是代理圖片小很多,因此載入完成的時間極短,如果在 CSS 讓代理圖片與原圖片的寬相同,則會形成一個佔住相同大小位置的圖片,一但原圖片載入完後,則將 url 從代理圖片置換成原圖片,因此不會看到畫面有跳動的情形。


Blog comments powered by Disqus