小tip-一種圖片載入狀態效果的實現

清夜發表於2018-11-05

做的一個需求,其中有一個是實現類似於下圖的一個圖片上傳效果:

小tip-一種圖片載入狀態效果的實現

從本地上傳圖片到伺服器,然後伺服器響應返回這個圖片在伺服器上的連結地址,將這個連結地址所對應的圖片顯示到螢幕上,並且在此圖片資源完全下載下來之前,呈現一個動態 loading的展點陣圖,直到圖片完全下載後進行替換

小tip

這是個很常見的需求,關鍵點在於檢測圖片的載入完成事件,一般的做法是,先將 img標籤的 src屬性指向一個當圖片載入完成後,再將 src指向真正的連結即可

const img = new Image()
// 真實圖片地址
const realSrc = 'http://a.com/real.png'
img.src = realSrc
img.onload = () => {
  // domImgElement 為 img 的 HTML DOM物件
  domImgElement.src = realSrc
}
複製程式碼

這種做法是沒什麼問題的,大多數情況下也都是這麼做的,不過由於我擅(xian)長(de)思(dan)考(teng),然後就想到一個小問題

我手頭的這個需求是基於 vue,在頁面渲染完畢之後,再想改變頁面上的內容,就需要重新渲染一遍頁面,儘管 vue存在 vnode的概念,更新速度很快,但就算再快都還是要把整個頁面刷一遍的,這個過程無論如何是跑不掉的,如果頁面上存在十幾張甚至幾十張類似於上面那種圖片非同步載入效果,那豈不是要改變幾十次 data值,vue內部就要對應 patch幾十次,然後頁面也就跟著整個更新幾十次,想想都覺得可怕

每多 patch一次 vnode tree,每多更新一次頁面,都將引起 CPUGPU 的能耗升高,日積月累四捨五入那就是一個龐大而隱形的能源消耗黑洞,對個人的發展、對公司的財務、對地球的溫室效應都將是一個沉重的負擔!

更何況,一般這種列表型別的圖片資料,都是存在於一個陣列中,每當有圖片下載完畢,在替換 src之前,還要先根據某個標誌位,例如圖片 id或者 url來從這個圖片陣列中找到對應的圖片,然後才能進行替換,而且這一步還要注意vue不自動對巢狀物件的屬性進行響應式操作的問題:

loadImg (imgId) {
  const img = new Image()
  // 真實圖片地址
  const realSrc = 'http://a.com/real.png'
  img.src = realSrc
  img.onload = () => {
    this.imgList.some(imgObj => {
      if (imgObj.id === imgId) {
        imgObj.src = realSrc
        this.$forceUpdate()
        return true
      }
      return false
    })
  }
}
複製程式碼

這只是一個小需求而已,為什麼要搞得這麼複雜?於是我決定最好簡單一點,不要那麼依賴 js,最好將大部分的工作交給 css來完成,因為相比之下,css的消耗更低

關鍵就是不進行替換 img標籤 src的操作,這樣一下子就能節省大半的 js操作,以下述 DOM結構為例:

<div class="img-item">
  <img :src="item.realUrl" alt="" />
</div>
複製程式碼

item.realUrl就是當前圖片的真實地址,拿到就直接賦值上去,後續也不會再操作 src這個屬性了,因為大部分工作移到了 css上:

.img-item {
  position: relative;
  width: 80px;
  height: 80px;
  background-color: #aeaeae;
}
.img-item::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 22px;
  height: 22px;
  margin-left: -11px;
  margin-top: -11px;
  /* 這是載入效果的圖片 */
  background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABAUlEQVQ4T6WSzyrEYRSGn2crF6GmJJTsLLGyEVM2lAshdriUsbAwsWeWlpJ/Kck1KNtXM330G8b84due8z7fed9z5J/P3/RJNoCDUt9XT3r1/gAkmVSfktwB00V0r84MBCS5AJaAc6A2EiDJOPBW+WUb2KtaSDKr3lYn6bKQ5AxYBS7V5WHy/QIkWSmCV/VhGHG7pwNIsg6cFlFdbfbZzlRHqI9VwCbQKKIt9bgPYKEArqqAMWCriBrq+0gWPpuTzAG7wKF68x2SpKY+99tC2/sa0FTr1cYkE8ALMK9ef9a+r7F9RDvAkdpK0ip+F0vY/SfoMXIXYOApDxvcrxn8BfABIiRjEYfmQAcAAAAASUVORK5CYII=') no-repeat;
  background-position: center center;
  background-size: contain;
  animation: loadpic .5s infinite linear;
  z-index: 1;
}

@keyframes loadpic {
  from { transform: rotate(0) }
  to { transform: rotate(360deg) }
}

img {
  position: relative;
  height: 100%;
  width: 100%;
  z-index: 2;
}
複製程式碼

.img-item是包裹單個 img的父元素,它的作用有兩個: 第一,撐開佔據的空間 第二,用它的 ::before偽元素來載入用於標識圖片正在載入的動效 loading圖,也就是說,將 loading圖放到了 ::before

::before會一直存在,直到 .box內的 img資源下載完畢後顯示出來,將其遮蓋住,因為 img元素的寬高與 .box相同,並且 imgz-index更大,所以只要 img沒有載入完畢,那麼就不會顯示,那麼就會顯示 ::before元素,也就是顯示 loading狀態,而只要 img載入完畢了,那麼就會顯示出來,就會遮蓋掉 ::before,這個過程視覺上看起來就能達到文章開頭那個圖片上的效果,而且消耗更低

emmmm,既然說到了圖片,那麼再順便說個關於 img標籤不太常用但有時候會比較有用的功能點吧

對於一張圖片:

<img src="http://a.com/b.png" />
複製程式碼

如果 src的指向沒有對應的資源或者不合法,那麼在瀏覽器上將會呈現一種特殊的樣式,例如:

小tip-一種圖片載入狀態效果的實現

不同的瀏覽器對於無效圖片的顯示可能也不一樣,在我的 Chrome上,就是上述這種破碎圖片的樣子,就是告訴你圖片找不到了,這種使用者體驗肯定是不太好的,對於這種情況,大部分的做法就是監聽 img標籤的 onerror事件,如果觸發了此事件,則說明失敗,然後就可以走失敗的邏輯了

不過這也是 js的方法,每個圖片都要設定一個監聽事件,那麼如果頁面上有幾十個圖片,豈不是要設定幾十個監聽事件?

每多設定一個監聽事件,都將引起...

這只是一個漸進增強的體驗,不應該投入那麼多的資源,於是需要再次藉助 css的能力,不監聽 error事件了,直接給需要載入失敗就友好顯示的圖片加上如下樣式:

img:before {
  /* 關鍵是這句 */
  content: '圖片載入失敗~';

  /* 下面都是輔助的樣式程式碼 */
  display: block;
  padding: 0 20px;
  background-color: #eaeaea;
  color: indianred;
  line-height: 30px;
}
複製程式碼

如果圖片載入失敗,那麼原先應該顯示破碎樣式的地方,將會顯示 圖片載入失敗~的提示文案

小tip-一種圖片載入狀態效果的實現

主要就是利用了 :before:after這兩個偽元素在 img標籤上的特性,img正常載入,則這兩個偽元素的內容就不顯示,如果 img載入失敗,就顯示這兩個偽元素的內容,這兩個偽元素的能力還是很多的,上面只是我隨便舉的一個例子

總結

我覺得 css對於前端工程師來說也是一項很重要的能力,別的不說,減少了 js的程式碼量,自然也就能減少報錯的概率,有時候還可以很輕鬆地實現一些需要一大堆 js才能實現的效果,提升頁面的效能

所以我有時候看到有的前端說 ta根本不 care CSS,認為這東西連程式語言都算不上,語法規範更是亂七八糟,學都懶得學,ta只需要 JS就能寫出任何效果來,特別是在如今各種 js框架大行其道的當下,很多初學者更是隻知 js而不知 css,甚至連 html都用不好,不僅是初學者,就算是一些工作了好些年的前端工程師,看到他們寫的程式碼的時候,我有時候也會驚歎於他們的 csshtml程式碼居然還可以這麼寫

大多數情況下這種現象或許也沒什麼毛病,畢竟屁股決定腦袋,就算是出去面試,面試官也幾乎只會問 js方面的問題,而不會問你 sectionarticle標籤適合在哪些情況下使用,字型的基線是怎麼回事,line-height又有什麼作用,我只是很納悶,作為一名前端開發工程師,前端三劍客只通其一,悲哀或許談不上,但三種技能開局就丟掉了倆,最起碼可以算是自毀功力了吧

相關文章