为了减小网络请求个数量,提升网站的访问速度,咱们通常都会把一些小的图片合并成一张sprite图,而后根据background-position来进行定位。在web端因为是固定的大小与left 、top,因此定位起来会比较准确、简单。可是在移动端就不同了,各类手机的屏幕大小不同,很难作到使用sprite图而后根据background-position来定位。因此广泛的作法都是使用单张图片,而后使用background-size: cover|100%|contain来控制背景图的大小。其实这样会简单得多,可是呢,若是图片太多,网速很差的状况下加载速度就惨不忍睹了。因此,根据以前的移动端适配之rem找到了解决方案。若是没有看过以前的文章,仍是建议去看一下。css
仍是以视觉稿为 640px为例,这是视觉稿的一部分:html
根据这个视觉稿,我切出来合并的sprite图张这样:git
这是640px视觉稿切出来的图,若是只是适配640px的屏幕,直接使用px定位彻底没有问题,可是考虑到其余的屏幕,因此咱们会使用rem来等比例缩放背景图。是的,把原尺寸转换为rem就能够了。代码以下:github
html代码结构web
1 <div class="test-sprites"> 2 <ul class="f-cb"> 3 <li class="icon1"></li> 4 <li class="icon2"></li> 5 <li class="icon3"></li> 6 <li class="icon4"></li> 7 <li class="icon5"></li> 8 <li class="icon6"></li> 9 </ul> 10 </div>
sass 代码sass
1 .test-sprites{ 2 margin-top: 30px; 3 4 ul{ 5 padding: 0; 6 margin: 0; 7 } 8 9 li{ 10 width: 0.48rem; 11 height: 0.7rem; 12 overflow: hidden; 13 border: 1px solid #ccc; 14 margin-left: 0.3rem; 15 float: left; 16 background:transparent url('http://nos.netease.com/edu-image/9BC0742AEB1A0B756EFC71B9DF77E452.png') 0 -0.02rem no-repeat; 17 background-size: 10.72rem 4.42rem; 18 } 19 20 .icon2{ 21 width: 0.74rem; 22 height: 0.64rem; 23 background-position: -1.88rem -0.05rem; 24 } 25 26 .icon3{ 27 width: 0.71rem; 28 height: 0.74rem; 29 background-position: -3.91rem -0.02rem; 30 } 31 32 .icon4{ 33 width: 0.72rem; 34 height: 0.73rem; 35 background-position: -5.91rem -0.03rem; 36 } 37 38 .icon5{ 39 width: 0.73rem; 40 height: 0.73rem; 41 background-position: -7.92rem -0.01rem; 42 } 43 44 .icon6{ 45 width: 0.67rem; 46 height: 0.57rem; 47 background-position: -9.96rem -0.08rem; 48 } 49 }
咱们日常使用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了那么多,见证奇迹的时候到了。那咱们直接上效果图:网站
看,是否是感受适配得还不错。对的,大致上是能够了,可是呢,认真看看有一些手机里面,总会发现有点缺陷,有些图片少了那么1px,其实若是要求不严格的话这样也就差很少了。可是本着精益求精的原则,咱们确定是须要解决这个问题的。因而在网上查找,功夫不负有心人,找到了林小志_linxz的这么一篇文章
其中比较关键的是:url
属性值为百分比时,将以图片的 中心点 为基准计算其相对位置,而使用px像素值时将以图片的 左上角(0 0)为基准。若是是10% 20%的这个值,那么就以图片的10% 20%的坐标点,放置在容器的10% 20%的位置。那这样理解的话,就是说明,若是是用百分比来做 background-position 的属性值的话,那么背景图片相对于容器的中心点是随时都在改变的。spa
加了background-position: 100% 100%;以后,就是把sprite图的B点移动到A点。如图
按照个人理解,就是把sprite图上的某一个点移动到元素上的某一个点,让这两个点重合。举个例子:我有一个400*400的div,200*200的sprite图,而后我给元素设置了background-position: 100% 100%;那么咱们先找出这两个点,元素的(100% 100%)点A就是元素的右下角,sprite图的(100% 100%)点B在sprite图的右下角。如图
,就是这么个状况。代码以下:
1 <style type="text/css"> 2 .box{ 3 width: 400px; 4 height: 400px; 5 border: 1px solid #ccc; 6 margin: 100px auto; 7 background: url(../images/200-1.jpg) no-repeat; 8 background-position: 100% 100%; 9 } 10 </style> 11 <div class="box"> 12 13 </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%;移动的步骤相似以下:
一、背景图片sprite图的中心点会改变成图片 10% 20% 这个点 即50*10% 50*20% 也就是(5px, 10px)(至关于把sprite图先移动(-5px -10px),也就是把sprite图的中心点移动端元素的左上角(0 0)处);
二、而后以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图,须要显示黄色的区块。
当咱们的div宽度为 100*100时,能够得出n:100%, m: 100%,因此咱们应该给元素加上background-position: 100% 100%;代码及效果以下:
1 <style type="text/css"> 2 .box{ 3 width: 100px; 4 height: 100px; 5 border: 1px solid #ccc; 6 margin: 100px auto; 7 background: url(../images/200-1.jpg) no-repeat; 8 background-position: 100% 100%; 9 } 10 </style> 11 <div class="box"> 12 </div>
100*100效果图
当div的宽度为 150*300时,能够得出n:200% m: -100%;因此咱们应该给元素加上background-position: 200% -100%;效果以下
150*300效果图
通过验证,上面的计算公式的确是能够的,就是那么简单。可是咱们也不能用一次就算一次吧,通过以上公式的整理,能够用sass写出一个fucntion 或者mixin,代码以下:
1 //$spriteWidth 雪碧图的宽度px 2 //$spriteHeight 雪碧图的高度px 3 //$iconWidth 须要显示icon的宽度px 4 //$iconHeight 须要显示icon的高度px 5 //$iconX icon的原始x坐标 6 //$iconY icon的原始y坐标 7 // 8 @mixin bgPosition($spriteWidth, $spriteHeight, $iconWidth, $iconHeight, $iconX, $iconY){ 9 10 background-position: (($iconX / ($spriteWidth - $iconWidth)) * 100% ($iconY / ($spriteHeight - $iconHeight)) * 100%); 11 } 12 13 //使用的方式 14 .test-sprites{ 15 margin-top: 30px; 16 17 ul{ 18 padding: 0; 19 margin: 0; 20 } 21 22 li{ 23 width: 0.48rem; 24 height: 0.7rem; 25 overflow: hidden; 26 box-sizing: border-box; 27 margin-left: 0.3rem; 28 float: left; 29 background:transparent url('http://nos.netease.com/edu-image/9BC0742AEB1A0B756EFC71B9DF77E452.png') 0 -0.02rem no-repeat; 30 background-size: 10.72rem 4.42rem; 31 } 32 33 .icon2{ 34 width: 0.74rem; 35 height: 0.64rem; 36 @include bgPosition(1072, 442, 74, 64, 188, 5); 37 } 38 39 .icon3{ 40 width: 0.71rem; 41 height: 0.74rem; 42 @include bgPosition(1072, 442, 71, 74, 391, 2); 43 } 44 45 .icon4{ 46 width: 0.72rem; 47 height: 0.73rem; 48 @include bgPosition(1072, 442, 72, 73, 591, 3); 49 } 50 .icon5{ 51 width: 0.73rem; 52 height: 0.73rem; 53 @include bgPosition(1072, 442, 73, 73, 792, 1); 54 } 55 .icon6{ 56 width: 0.67rem; 57 height: 0.57rem; 58 @include bgPosition(1072, 442, 67, 57, 996, 8); 59 } 60 }
就是下面这个图片,按照这样的方式,通过实践是没有问题的
原始sprite图 1072*442
可是呢,以上的bgPosition感受不够简洁,由于每次都要输入sprite图的宽高,那么能够在bgPosition的基础上再拓展一下;
1 //同一张sprite图,横图 2 @mixin bgPositionSameSprite($iconWidth, $iconHeight, $iconX, $iconY){ 3 4 $spriteWidth : 1072; 5 $spriteHeight : 442; 6 7 @include bgPosition($spriteWidth, $spriteHeight, $iconWidth, $iconHeight, $iconX, $iconY); 8 } 9 10 //同一张sprite图、竖图 11 @mixin bgPositionSameSprite-tow($iconWidth, $iconHeight, $iconX, $iconY){ 12 13 $spriteWidth : 300; 14 $spriteHeight : 1000; 15 16 @include bgPosition($spriteWidth, $spriteHeight, $iconWidth, $iconHeight, $iconX, $iconY); 17 }
这样咱们就只要输入四个参数了,那还能不能再简洁点呢,那只能看状况了,若是你的每一个icon大小都同样的话,是彻底没有问题的,你只须要输入每一个icon的坐标就行,好比这样的图
原始分享图片 220*220
而后你就能够写这样的一个方法.
1 //同一张sprite图而且每一个icon的大小相同 2 @mixin bgPositionSameSpriteAndWidth($iconX, $iconY){ 3 4 $spriteWidth : 220; 5 $spriteHeight : 220; 6 $iconWidth : 61; 7 $iconHeight : 61; 8 9 @include bgPosition($spriteWidth, $spriteHeight, $iconWidth, $iconHeight, $iconX, $iconY); 10 } 11 //使用 12 i{ 13 padding-top: 100%; 14 width: 100%; 15 display: block; 16 background: url(http://nos.netease.com/edu-image/3A65D313376F13CE75CE01C2593BD1CE.png) 0 0 no-repeat; 17 background-size: 2.2rem 2.2rem; 18 } 19 20 .i-sina{ 21 @include bgPositionSameSpriteAndWidth(10, 10); 22 } 23 24 .i-qzone{ 25 @include bgPositionSameSpriteAndWidth(80, 10); 26 } 27 28 .i-qq{ 29 @include bgPositionSameSpriteAndWidth(150, 10); 30 } 31 32 .i-douban{ 33 @include bgPositionSameSpriteAndWidth(10, 80); 34 } 35 36 .i-yixin{ 37 @include bgPositionSameSpriteAndWidth(80, 80); 38 } 39 40 .i-renren{ 41 @include bgPositionSameSpriteAndWidth(150, 80); 42 }
以上方式方案能够完美解决适配定位问题,还能解放生产力。