移動端適配之雪碧圖(sprite)背景圖片定位

為了減少網(wǎng)絡(luò)請求個數(shù)量,提高網(wǎng)站的訪問速度,我們一般都會把一些小的圖片合并成一張sprite圖,然后根據(jù)background-position來進(jìn)行定位。在web端由于是固定的大小與left 、top,所以定位起來會比較準(zhǔn)確、簡單。但是在移動端就不一樣了,各種手機(jī)的屏幕大小不一樣,很難做到使用sprite圖然后根據(jù)background-position來定位。所以普遍的做法都是使用單張圖片,然后使用background-size: cover|100%|contain來控制背景圖的大小。其實(shí)這樣會簡單得多,但是呢,如果圖片太多,網(wǎng)速不好的情況下加載速度就慘不忍睹了。所以,根據(jù)之前的移動端適配之rem找到了解決方案。如果沒有看過之前的文章,還是建議去看一下。

還是以視覺稿為 640px為例,這是視覺稿的一部分:

移動端視覺稿

根據(jù)這個視覺稿,我切出來合并的sprite圖張這樣:

原始sprite圖 1072*442

這是640px視覺稿切出來的圖,如果只是適配640px的屏幕,直接使用px定位完全沒有問題,但是考慮到其他的屏幕,所以我們會使用rem來等比例縮放背景圖。是的,把原尺寸轉(zhuǎn)換為rem就可以了。代碼如下:

html代碼結(jié)構(gòu)
    <div class="test-sprites">
        <ul class="f-cb">
            <li class="icon1"></li>
            <li class="icon2"></li>
            <li class="icon3"></li>
            <li class="icon4"></li>
            <li class="icon5"></li>
            <li class="icon6"></li>
        </ul>
    </div>

sass 代碼

.test-sprites{
    margin-top: 30px;

    ul{
        padding: 0;
        margin: 0;
    }

    li{
        width: 0.48rem;
        height: 0.7rem;
        overflow: hidden; 
        border: 1px solid #ccc; 
        margin-left: 0.3rem;
        float: left;
        background:transparent url('http://nos.netease.com/edu-image/9BC0742AEB1A0B756EFC71B9DF77E452.png') 0 -0.02rem no-repeat;
        background-size: 10.72rem 4.42rem;
    }

    .icon2{
        width: 0.74rem; 
        height: 0.64rem;
        background-position: -1.88rem -0.05rem;
    }

    .icon3{
        width: 0.71rem;
        height: 0.74rem;
        background-position: -3.91rem -0.02rem;
    }

    .icon4{
        width: 0.72rem;
        height: 0.73rem;
        background-position: -5.91rem -0.03rem;
    }

    .icon5{
        width: 0.73rem;
        height: 0.73rem;
        background-position: -7.92rem -0.01rem;
    }

    .icon6{
        width: 0.67rem;
        height: 0.57rem;
        background-position: -9.96rem -0.08rem;
    }
}

我們平常使用background-size: 100%|cover|contain只是根據(jù)元素的寬高進(jìn)行縮放的,那只對單張圖片有用,因?yàn)榇藭r百分比的大小是相對于元素的大小的,也就是說,一個100px*100px的div,使用一張1000px*1000px的sprite圖做背景圖片的,如果此時你給div加上background-size: 100%|cover|contain的話,那么整張sprite圖就會被壓縮成100*100的大小,這恐怕不是你想要的吧。

而我們的sprite是要根據(jù)它的原始大小來進(jìn)行縮放的,先把px轉(zhuǎn)化為rem,按照我的習(xí)慣是直接除以100px。也就得到類似background-size: 10.72rem 4.42rem;(原始1072px*442px)。這樣sprite圖就可以根據(jù)font-size進(jìn)行縮放了。而且定位background-position直接使用原始的left-top 值除以100px就可以了,就是那么簡單,BB了那么多,見證奇跡的時候到了。那我們直接上效果圖:

適配結(jié)果.png

看,是不是感覺適配得還不錯。對的,大體上是可以了,但是呢,認(rèn)真看看有一些手機(jī)里面,總會發(fā)現(xiàn)有點(diǎn)缺陷,有些圖片少了那么1px,其實(shí)如果要求不嚴(yán)格的話這樣也就差不多了。但是本著精益求精的原則,我們肯定是需要解決這個問題的。于是在網(wǎng)上查找,功夫不負(fù)有心人,找到了林小志_linxz的這么一篇文章
其中比較關(guān)鍵的是:

屬性值為百分比時,將以圖片的 中心點(diǎn) 為基準(zhǔn)計算其相對位置,而使用px像素值時將以圖片的 左上角(0 0)為基準(zhǔn)。如果是10% 20%的這個值,那么就以圖片的10% 20%的坐標(biāo)點(diǎn),放置在容器的10% 20%的位置。那這樣理解的話,就是說明,如果是用百分比來作 background-position 的屬性值的話,那么背景圖片相對于容器的中心點(diǎn)是隨時都在改變的。

按照我的理解,就是把sprite圖上的某一個點(diǎn)移動到元素上的某一個點(diǎn),讓這兩個點(diǎn)重合。舉個例子:我有一個400*400的div,200*200的sprite圖,然后我給元素設(shè)置了background-position: 100% 100%;那么我們先找出這兩個點(diǎn),元素的(100% 100%)點(diǎn)A就是元素的右下角,sprite圖的(100% 100%)點(diǎn)B在sprite圖的右下角。如圖

Paste_Image.png

加了background-position: 100% 100%;之后,就是把sprite圖的B點(diǎn)移動到A點(diǎn)。如圖

400.jpg

,就是這么個情況。代碼如下:

<style type="text/css">
    .box{
        width: 400px;
        height: 400px;
        border: 1px solid #ccc;
        margin: 100px auto;
        background: url(../images/200-1.jpg) no-repeat;
        background-position: 100% 100%;
    }
</style>
<div class="box">
    
</div>

但是這個是在我們知道百分比的情況下的,而我們需要做positon定位的時候需要的正是這個百分比,所以我們應(yīng)該根據(jù)其他的變量把百分比求出來。我們可以把以上的情況轉(zhuǎn)換成跟元素、sprite圖、坐標(biāo)有關(guān)的場景,比如類似:

當(dāng)給元素加background-position: 10% 20%的時候,sprite圖會先參考自身移動(-10% -20%)也就是改變自己的中心點(diǎn),然后再根據(jù)元素的寬高來移動(10% 20%),最后sprite圖會移動到一個點(diǎn)(X Y)。這個點(diǎn)就是我們需要顯示在元素中icon的坐標(biāo)。根據(jù)這個方式,總是可以把sprite圖的某個點(diǎn)定位到元素的(0 0)位置。具體操作的demo

那重新梳理一下:
如果使用px來定位的話,那么sprite圖是以左上角(0 0) 為中心點(diǎn)的,比如加上background-position:10px 20px;(此時sprite圖的中心點(diǎn)在(0 0))那么圖片的左邊緣跟上邊緣會往下移動20px,往右移動10px;我們平常使用的也是這種情況。

但是使用百分比來定位的話,那圖片就不是以左上角為中心點(diǎn)了。比如加上background-position:10% 20%;那么背景sprite圖片的中心點(diǎn)就會改變成圖片 (10% 20%) 這個點(diǎn)了,比如原始sprite圖片寬度為50px*50px,原始原始的中心點(diǎn)是(0 0);加了background-position:10% 20%;之后呢,中心點(diǎn)就變成了 (50px*10%  50px*20%) 也就是(5px, 10px)這個點(diǎn),然后就會根據(jù)這個中心點(diǎn)來進(jìn)行移動,假設(shè)元素的大小為200px*200px,根據(jù)推理,加了background-position:10% 20%;移動的步驟類似如下:

1、背景圖片sprite圖的中心點(diǎn)會改變成圖片 10% 20% 這個點(diǎn) 即50*10% 50*20% 也就是(5px, 10px)(相當(dāng)于把sprite圖先移動(-5px -10px),也就是把sprite圖的中心點(diǎn)移動端元素的左上角(0 0)處);

2、然后以sprite圖的(5px 10px)點(diǎn)為中心點(diǎn)移動元素寬高度的10% 元素高度的20%,也就是往右往下移動了 20px 40px;
需要注意的是,這次的移動是以(5px 10px)這個中心點(diǎn)來移動的,就是把這個點(diǎn)先移動到父元素 0 0 的位置,再移動 20px 40px;
所以最終移動的距離是 (-5px+20px -10px+40px) 也就是 向右移動了15px 向下移動了30px 。

根據(jù)以上的推理,要想得到定位的百分比值(n m),我們需要 元素的寬高(w h), sprite圖的寬高(k g),我們需要顯示icon的坐標(biāo)(x y),我們以向右向下移動端為正,向上向左為負(fù)??梢缘玫接嬎愎饺缦拢?/p>

left: -n* k + n*w = -x
top: -m* g + m*h = -y

根據(jù)上面的公式,我們可以得到:

n = -x / (w-k) * 100%
m = -y / (h-g) * 100%

那舉個例子:我們有一張200*200的sprite圖,需要顯示黃色的區(qū)塊。


背景圖200*200
  1. 當(dāng)我們的div寬度為 100*100時,可以得出n:100%, m: 100%,所以我們應(yīng)該給元素加上background-position: 100% 100%;代碼及效果如下:
<style type="text/css">
    .box{
        width: 100px;
        height: 100px;
        border: 1px solid #ccc;
        margin: 100px auto;
        background: url(../images/200-1.jpg) no-repeat;
        background-position: 100% 100%;
    }
</style>
<div class="box">
</div>
100*100效果圖
  1. 當(dāng)div的寬度為 150*300時,可以得出n:200% m: -100%;所以我們應(yīng)該給元素加上background-position: 200% -100%;效果如下
150*300效果圖

經(jīng)過驗(yàn)證,上面的計算公式的確是可以的,就是那么簡單。但是我們也不能用一次就算一次吧,經(jīng)過以上公式的整理,可以用sass寫出一個fucntion 或者mixin,代碼如下:

//$spriteWidth 雪碧圖的寬度px
//$spriteHeight 雪碧圖的高度px
//$iconWidth 需要顯示icon的寬度px
//$iconHeight 需要顯示icon的高度px
//$iconX icon的原始x坐標(biāo)
//$iconY icon的原始y坐標(biāo)
//
@mixin bgPosition($spriteWidth, $spriteHeight, $iconWidth, $iconHeight, $iconX, $iconY){

    background-position: (($iconX / ($spriteWidth - $iconWidth)) * 100% ($iconY / ($spriteHeight - $iconHeight)) * 100%); 
}

//使用的方式
.test-sprites{
    margin-top: 30px;

    ul{
        padding: 0;
        margin: 0;
    }

    li{
        width: 0.48rem;
        height: 0.7rem;
        overflow: hidden; 
        box-sizing: border-box; 
        margin-left: 0.3rem;
        float: left;
        background:transparent url('http://nos.netease.com/edu-image/9BC0742AEB1A0B756EFC71B9DF77E452.png') 0 -0.02rem no-repeat;
        background-size: 10.72rem 4.42rem;
    }

    .icon2{
        width: 0.74rem; 
        height: 0.64rem;
        @include bgPosition(1072, 442, 74, 64, 188, 5); 
    }

    .icon3{
        width: 0.71rem;
        height: 0.74rem;
        @include bgPosition(1072, 442, 71, 74, 391, 2);
    }

    .icon4{
        width: 0.72rem;
        height: 0.73rem;
        @include bgPosition(1072, 442, 72, 73, 591, 3); 
    }
    .icon5{
        width: 0.73rem;
        height: 0.73rem;
        @include bgPosition(1072, 442, 73, 73, 792, 1); 
    }
    .icon6{
        width: 0.67rem;
        height: 0.57rem;
        @include bgPosition(1072, 442, 67, 57, 996, 8);
    }
}

就是下面這個圖片,按照這樣的方式,經(jīng)過實(shí)踐是沒有問題的


原始sprite圖 1072*442

但是呢,以上的bgPosition感覺不夠簡潔,因?yàn)槊看味家斎雜prite圖的寬高,那么可以在bgPosition的基礎(chǔ)上再拓展一下;

//同一張sprite圖,橫圖
@mixin bgPositionSameSprite($iconWidth, $iconHeight, $iconX, $iconY){

    $spriteWidth : 1072;
    $spriteHeight : 442;

    @include bgPosition($spriteWidth, $spriteHeight, $iconWidth, $iconHeight, $iconX, $iconY);
}

//同一張sprite圖、豎圖
@mixin bgPositionSameSprite-tow($iconWidth, $iconHeight, $iconX, $iconY){

    $spriteWidth : 300;
    $spriteHeight : 1000;

    @include bgPosition($spriteWidth, $spriteHeight, $iconWidth, $iconHeight, $iconX, $iconY);
}

這樣我們就只要輸入四個參數(shù)了,那還能不能再簡潔點(diǎn)呢,那只能看情況了,如果你的每個icon大小都一樣的話,是完全沒有問題的,你只需要輸入每個icon的坐標(biāo)就行,比如這樣的圖

原始分享圖片 220*220

然后你就可以寫這樣的一個方法

//同一張sprite圖并且每個icon的大小相同
@mixin bgPositionSameSpriteAndWidth($iconX, $iconY){

    $spriteWidth : 220;
    $spriteHeight : 220;
    $iconWidth : 61;
    $iconHeight : 61;

    @include bgPosition($spriteWidth, $spriteHeight, $iconWidth, $iconHeight, $iconX, $iconY);
}
//使用
i{
    padding-top: 100%;
    width: 100%;
    display: block;
    background: url(http://nos.netease.com/edu-image/3A65D313376F13CE75CE01C2593BD1CE.png) 0 0 no-repeat;
    background-size: 2.2rem 2.2rem;
}

.i-sina{
    @include bgPositionSameSpriteAndWidth(10, 10);
}

.i-qzone{
    @include bgPositionSameSpriteAndWidth(80, 10);
}

.i-qq{
    @include bgPositionSameSpriteAndWidth(150, 10);
}

.i-douban{
    @include bgPositionSameSpriteAndWidth(10, 80);
}

.i-yixin{
    @include bgPositionSameSpriteAndWidth(80, 80);
}

.i-renren{
    @include bgPositionSameSpriteAndWidth(150, 80);
}

以上方案基本可以解決適配定位問題,還能解放生產(chǎn)力。由于瀏覽器對小數(shù)點(diǎn)的取舍而出現(xiàn)圖片被截掉1px ,建議該圖標(biāo)的div稍微比圖標(biāo)大一點(diǎn),或者加padding。如果有不對的地方,還望指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載請聲明 原文鏈接地址 關(guān)注公眾號獲取更多資訊 第一部分 HTML 第一章 職業(yè)規(guī)劃和前景 職業(yè)方向規(guī)劃定位...
    前端進(jìn)階之旅閱讀 16,590評論 32 459
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,784評論 1 92
  • 選擇qi:是表達(dá)式 標(biāo)簽選擇器 類選擇器 屬性選擇器 繼承屬性: color,font,text-align,li...
    wzhiq896閱讀 1,787評論 0 2
  • 1、屬性選擇器:id選擇器 # 通過id 來選擇類名選擇器 . 通過類名來選擇屬性選擇器 ...
    Yuann閱讀 1,655評論 0 7
  • 如果有來生,你最想做點(diǎn)啥? 如果有來生,我一定會在最初懂得臭美的年紀(jì),纏著媽媽給我買幾套合身又漂亮的衣服;...
    伊七七閱讀 320評論 0 0