雪碧圖定位


/* 雪碧圖定位 */
@mixin bgPosition($spriteWidth, $spriteHeight, $iconWidth, $iconHeight, $iconX, $iconY){
    background-position: (($iconX / ($spriteWidth - $iconWidth)) * 100% ($iconY / ($spriteHeight - $iconHeight)) * 100%); 
}
@mixin bgPositionSameSprite($iconWidth, $iconHeight, $iconX, $iconY){
    $spriteWidth : 300;
    $spriteHeight : 600;
    @include bgPosition($spriteWidth, $spriteHeight, $iconWidth, $iconHeight, $iconX, $iconY);
}
%icons {
    background-image: url(i/icons.png);
    background-repeat: no-repeat;
    background-size: 3rem 6rem;
}

.pic{
        @extend %icons;
        @include bgPositionSameSprite(62, 62, 238, 420);
        width: 0.62rem;
        height: 0.62rem;
        display: inline-block;
        vertical-align: middle;
        margin: 0 0.1rem 0 0.15rem;
    }
$iconX,$iconY----在圖片中的位置
$iconWidth, $iconHeight----該圖片本身的大小
$spriteWidth,$spriteHeight----整個圖層的大小
在圖片中的位置/(整個圖層的大小-該圖片本身的大小)*100%
238/(300-62)

原理:

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

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


移動端視覺稿


根據這個視覺稿,我切出來合並的sprite圖張這樣:


原始sprite圖 1072*442

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

html代碼結構
    <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只是根據元素的寬高進行縮放的,那只對單張圖片有用,因為此時百分比的大小是相對於元素的大小的,也就是說,一個100px*100px的div,使用一張1000px*1000px的sprite圖做背景圖片的,如果此時你給div加上background-size: 100%|cover|contain的話,那么整張sprite圖就會被壓縮成100*100的大小,這恐怕不是你想要的吧。

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


適配結果.png

看,是不是感覺適配得還不錯。對的,大體上是可以了,但是呢,認真看看有一些手機里面,總會發現有點缺陷,有些圖片少了那么1px,其實如果要求不嚴格的話這樣也就差不多了。但是本着精益求精的原則,我們肯定是需要解決這個問題的。於是在網上查找,功夫不負有心人,找到了林小志_linxz的這么一篇文章
其中比較關鍵的是:

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

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


Paste_Image.png


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


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定位的時候需要的正是這個百分比,所以我們應該根據其他的變量把百分比求出來。我們可以把以上的情況轉換成跟元素、sprite圖、坐標有關的場景,比如類似:

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

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

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

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

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

根據以上的推理,要想得到定位的百分比值(n m),我們需要 元素的寬高(w h), sprite圖的寬高(k g),我們需要顯示icon的坐標(x y),我們以向右向下移動端為正,向上向左為負。可以得到計算公式如下:

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

根據上面的公式,我們可以得到:

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

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


背景圖200*200
  1. 當我們的div寬度為 100*100時,可以得出n:100%, m: 100%,所以我們應該給元素加上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. 當div的寬度為 150*300時,可以得出n:200% m: -100%;所以我們應該給元素加上background-position: 200% -100%;效果如下

150*300效果圖


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

//$spriteWidth 雪碧圖的寬度px //$spriteHeight 雪碧圖的高度px //$iconWidth 需要顯示icon的寬度px //$iconHeight 需要顯示icon的高度px //$iconX icon的原始x坐標 //$iconY icon的原始y坐標 // @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); } }

就是下面這個圖片,按照這樣的方式,經過實踐是沒有問題的


原始sprite圖 1072*442

但是呢,以上的bgPosition感覺不夠簡潔,因為每次都要輸入sprite圖的寬高,那么可以在bgPosition的基礎上再拓展一下;

//同一張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); }

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


原始分享圖片 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); }

以上方式方案可以完美解決適配定位問題,還能解放生產力。如果有不對的地方,還望指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM