一、概述
在傳統的居中布局時,我們常用background-position這個屬性來進行雪碧圖的定位,在減少數據量的同時,保證准確定位。在移動端使用越來越重的現在,以往的傳統定位,已經無法達到目的,那么是否有合適的解決方案呢?答案是有的,讓我們先來了解background的兩個屬性:
background-position:背景圖片相對容器原點的起始位置。詳解可以查看另一篇博客:background-position 詳解。
background-size: 規定背景圖的尺寸;
語法:background-size: width height;
二、子圖尺寸相同
background-position: a% b%; =》 background-position: x y; 其中:containerWidth為容器寬,containerHeight為容器高,bgWidth為雪碧圖寬,bgHeight為雪碧圖寬,
則存在(公式1): x = (containerWidth - bgWidth) * a% y = (containerHeight - bgHeight) * b%


2.1、場景1:傳統布局
傳統布局下,容器尺寸與子圖的尺寸相同,由公式1可推出以下結論:
由於子圖尺寸相同,得出(公式2):
bgWidth = xTotal * containerWidth; bgHeight = yTotal * containerHeight;
其中xTotal為雪碧圖橫向的子圖數量,yTotal為雪碧圖縱向的子圖數量。
由公式1、公式2可推導出(公式3):
a% = x / (containerWidth - bgWidth)
= x / (containerWidth - xTotal * containerWidth)
= x / (1 - xTotal) * containerWidth
b% = y / (containerHeight - bgHeight)
= y / (containerHeight - yTotal * containerHeight)
= y / (1 - yTotal) * containerHeight
定位時,背景圖的起始點,時常在某個子圖的起始點上,所以可以推導出(公式4):
a% = x / (1 - xTotal) * containerWidth
= n * containerWidth / (1 - xTotal) * containerWidth
= n / (1 - xTotal)
b% = y / (1 - yTotal) * containerHeight
= m * containerHeight / (1 - yTotal) * containerHeight
= m / (1 - yTotal)
其中,m、n一定是一個小於等於0的數。
需要得到預期結果,使用公式4,
起始位置:x=-1 * containerWidth,y = 0 * containerHeight;
so: n=-1,m=0;
子圖數:xTotal = 3; yTotal = 2;
要得到第二張圖的顯示定位應該為:
a% = n / (1 - xTotal) = -1 / (1 - 3) = 50%
b% = m / (1 - yTotal) = 0 / 1 - 2 = 0%
width: 218px;
height: 218px;
background-position: 50% 0;
background-repeat: no-repeat;
2.2、場景2:響應式布局下
回顧公式1:
x = (containerWidth - bgWidth) * a%
y = (containerHeight - bgHeight) * b%
響應式情景下,containerWidth會縮放,而此時bgWidth的值不會變化,使得公式2無法得到。
解決該問題的關鍵是,使得bgWidth的大小,隨着containerWidth的變化而變化。
再回過頭看看background-size這個屬性,它可以使得背景圖根據容器的變化而變化,且它的百分比屬性可以以父元素的百分比來設置背景圖像的寬度和高度,從而達到父容器與背景圖的一個對應關系。
如果需要獲得公式2,那么需要將背景圖按照怎樣的方式變化呢?
當 background-size: 100% auto;時,得到的結果:
圖2.3 100%
由此可看出,當xpos為100%時,背景圖縮放到父容器相同大小。
當xpos為300%時,背景圖縮放到父容器的3倍大,此時背景圖與父容器的關系同場景一相同,從而可以應用公式2,此時的xTotal既代表子圖橫向的數量,也表示背景圖橫向的收縮比。
由此當是如下代碼,亦可得到預期的第二張圖:
width: 118px;
height: 118px;
background-size: 300% auto;
background-position: 50% 0;
background-repeat: no-repeat;
總結:在響應式下,且子圖尺寸相同時,將background-size 的縮放比設置成與子圖數量相同,再通過background-position可實現輕松定位。x,y任意方向設置縮放比,另一方向可使用auto值,實現背景圖的等比例縮放。此例中,雖然子圖的形狀為正方形,但實際操作中,不要求。矩形一樣可以按照同樣的公式實現,因為縱軸和橫軸的計算都是分開的,相互沒有影響。
三、子圖尺寸不同
在子圖尺寸不同的情況下,通常我們不會使用百分比定位,而是選擇具體值定位。 示例:
圖3.1 不規則雪碧圖
取到白色遮罩覆蓋的花環:
圖3.2 目標子圖
3.1、場景3:傳統布局下的定位
代碼如下:
border: 1px solid red;
width: 282px;
height: 301px;
background-position: -369px -394px;
background-repeat: no-repeat;
直接使用具體值定位,較為簡單,容器盒子的尺寸與目標子圖的尺寸相同,定位位置為容器盒子在背景圖中的偏移位置,結果與預期相符。
3.2、場景4:響應式下的定位
回到場景1,其中有三個定位的關鍵因素:
a、盒子尺寸;
b、盒子相對背景圖的偏移位置;
c、背景圖尺寸(決定目標子圖尺寸、從而決定盒子尺寸);
在響應式下,通常盒子的尺寸會跟隨屏幕的伸縮而變化,如何使得最初的定位,不會因為盒子尺寸的變化而發生改變呢?
回到示例圖3.1,想象一下如果我們將背景圖、容器盒子、定位位置三者一起進行伸縮,那么會怎么樣呢?定位仍然在目標子圖,不會發生變化。
在變化過程中,只要缺少一個因素,就會發生錯位。因此解決問題的關鍵是:三個因素,等比例伸縮。
我們看下賦值:
盒子伸縮:width: newWidth; height: newHeight;
偏移伸縮:background-position: newX newY;
背景圖尺寸: background-size: newWidth/auto auto/newHeight;
關於幾個新值的計算:是一個換算過程,方法有多種,可以按照具體項目的換算方法進行。我使用的是hotcss的方法,只需要給出原尺寸,便可輕松實現轉換,這里不再展開,有興趣的同學,可以上github了解:hotcss。
圖3.3 響應式下的定位
調試器中給出的代碼是hotcss編譯之后的結果,源碼如下(px2rem為hotcss的一個轉換函數):
.test { margin: px2rem(100); border: 1px solid red; width: px2rem(282); height: px2rem(301); background-position: px2rem(-369) px2rem(-394); background-size: px2rem(1024) auto; background-image: url($imgpath + "temp/flower-3-new.jpg"); background-repeat: no-repeat }
總結:雪碧圖的四種情況在本文中都給出了解決方案,場景1、2給出了子圖尺寸相同時的定位,其中使用到了%,大大減少了計算。%的使用,讓我們不再關注原來的尺寸,這個換算的過程映射到了基於一種相對比例關系(子圖數量)進行的計算。場景3、4解決了子圖不同的情況,這時,需要我們拿到三組關鍵值:原背景圖大小、原容器大小、原相對位移,再利用關鍵值實現伸縮后的映射,從而實現定位。