回想n年前剛寫前端的時候,在處理一個’滑鼠hover切換背景圖會閃’的問題時,將兩張背景圖合成一張圖片,順利解決問題。這應該是我第一次用到雪碧圖的情況。
如今,開啟一個站點,呈現鋪天蓋地的圖片資源的頁面隨處可見。而多數站點更會用一套包含幾十個風格統一的圖示的圖示庫,加之移動端的佔比與日俱增,雪碧圖這項技術被運用的就越來越普遍。
最簡單,最實用的使用方法
得益於偽元素的功勞,在不破壞頁面結構,不增加多餘標籤的情況下,通過::after建立一個你所需要圖示大小的偽元素,並將所需要的圖示通過background-position定位到指定的空間,對應的圖示變順利地呈現出來。
1 2 3 4 5 6 7 8 9 10 11 12 |
.message:after { background: url(../img/sprite.png) scroll 0px -86px no-repeat transparent; content: ''; text-indent: -9999px; overflow: hidden; position: absolute; top: 0; left: 50%; margin-left: -10px; width: 20px; height: 22px; } |
一些問題
看上去貌似是一個完美的解決方案,然而真的是這樣麼?我們來看一個例子:
目前有這麼一個簡單的頁面(無視它的設計合理性吧),拿到手後擼起袖子就寫。切圖切著切著貌似哪裡不對。。。恩!三個鈴鐺不是同一個圖片大小不同而已麼?明顯有優化的餘地啊!只要切一個圖來個調整下尺寸不就解決了麼~~
恩恩恩。在普通的頁面中,來個backgroud-size妥妥地解決。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
.message { background: url(../img/message.png) no-repeat; } .large { background-size: 64px 64px; } .normal { background-size: 32px 32px; } .small { background-size: 16px 16px; } |
當我們按部就班地把屬性設定到雪碧圖上時,可以發現,呈現出的效果完全不是之前設想的那樣。尺寸、定位完全不對。哪裡出了問題了呢?原理很簡單。background-size並不感知icon的存在,我們想只針對icon進行配置,而這個屬性其實是作用於整個雪碧圖上的。
隨之而來的解決方案
回想整個渲染執行的流程:
- background-size 作用於整個雪碧圖,對其尺寸縮放。
- background-position 定位。
那麼要得到正確的效果的話,以放大兩倍為例,需要做到:
- 將偽元素的尺寸擴大2倍
1234.message:after {width: originWidth * 2;height: originHeight * 2;} - 將整個雪碧圖的尺寸擴大2倍
123.message:after {background-size: originSpriteWidth * 2, originspriteHeight * 2;} - 將座標偏移量相應擴大2倍
123.message:after {background-position: originBackgroundPositionX * 2, originBackgroundPositionY * 2;}
綜上,如果想在雪碧圖內實現縮放邏輯,必須通過
- 雪碧圖長寬
- 該圖示原始長寬
- 該圖示在雪碧圖中的偏移量
總共6個變數去實現。用動態樣式語言的話,或許可以得到這麼一個通用函式:
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 |
/* 雪碧圖icon img: 圖片路徑 spriteWidth: 合成的雪碧圖的寬度 spriteHeight: 合成的雪碧圖的長度 originWidth: 使用圖片的原始寬度 originHeight: 使用圖片的原始長度 width: 需要呈現的寬度 height: 需要呈現的長度 offsetX: 雪碧圖中的x軸偏移位置 offsetY: 雪碧圖中的y軸偏移位置 horizontal: 水平定位 hDuration: 水平定位偏移 vertical: 垂直定位 vDuration: 垂直定位偏移 relativePos: 相對定位 */ .pseudo-icon-sprite (@img, @spriteWidth, @spriteHeight, @originWidth, @originHeight, @width, @height, @offsetX, @offsetY, @horizontal:left, @hDuration:0, @vertical:center, @vDuration:0, @relativePos:relative) { ... } |
以上,我們完成了把雪碧圖中的圖示縮放後呈現在頁面上的這一目標。
怎麼樣,看到這個解決方案,有沒有一種把程式碼刪光,把大中小三個圖示都塞進雪碧圖的衝動?
冷靜一下,再想想方案
不就是縮放麼?background-size會扯出這麼多問題,不是還有個transform麼?會出現同樣的問題麼? 那麼要得到正確的效果的話,以放大兩倍為例,需要做到: 恩?好像就加一行程式碼?
1 2 3 4 |
.message:after { ... transform: scale(2); } |
好吧,就是這樣。兩者區別也很簡單,因為一個作用在元素上,一個作用在雪碧圖上,所以後者會帶出n多副作用。
那麼如果需要縮放到固定尺寸時,還需要知曉原始尺寸,通過計算得到一個縮放係數,這樣才能最終達到所需的效果。
最後的總結
回顧一下,一個簡單的縮放需求,出現了三種解決方案:
- 最無腦的全部放進雪碧圖中
- 最冗長的修改background-size
- 以及最簡短的transform變形
一般情況下,還是無視第二種方案吧。那麼在1和3兩者中間,則各有取捨。現在回想整個尋求解決方案的過程,個人還是比較傾向方案1的,畢竟,幾乎沒有什麼出錯的可能。而且都是同一張雪碧圖,並沒有更多的請求數,只是多了點圖片大小而已。