图片 7

雪碧图在缩放场景下的特殊处理

Posted by

雪碧图在缩放场景下的特殊处理

2016/07/13 · CSS ·
雪碧图

原文出处: 百度EFE   

回想n年前刚写前端的时候,在处理一个’鼠标hover切换背景图会闪’的问题时,将两张背景图合成一张图片,顺利解决问题。这应该是我第一次用到雪碧图的情况。

图片 1

雪碧图作为背景在切换时不会有因为需要等待下载而产生的闪现

如今,打开一个站点,呈现铺天盖地的图片资源的页面随处可见。而多数站点更会用一套包含几十个风格统一的图标的图标库,加之移动端的占比与日俱增,雪碧图这项技术被运用的就越来越普遍。

1、困扰多时的问题

最简单,最实用的使用方法

得益于伪元素的功劳,在不破坏页面结构,不增加多余标签的情况下,通过::after创建一个你所需要图标大小的伪元素,并将所需要的图标通过background-position定位到指定的空间,对应的图标变顺利地呈现出来。

CSS

.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; }

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;
}

图片 2

通过伪元素实现的图标

在这之前做Web
App开发的的时候,在自适应方面一般都是宽度通过百分比,高度以iPhone6跟iPhone5之间的一个平衡值写死,我们的设计稿都是iPhone5的640
*
1136标准,所以高度一般取个大概值,各种图标的宽高也是取平衡值写死,然后部分样式通过媒体查询来设置,例如背景图的多倍图、基础字体大小、图标宽高。

一些问题

看上去貌似是一个完美的解决方案,然而真的是这样么?我们来看一个例子:

图片 3

一个界面

目前有这么一个简单的页面(无视它的设计合理性吧),拿到手后撸起袖子就写。切图切着切着貌似哪里不对。。。恩!三个铃铛不是同一个图片大小不同而已么?明显有优化的余地啊!只要切一个图来个调整下尺寸不就解决了么~~

恩恩恩。在普通的页面中,来个backgroud-size妥妥地解决。

CSS

.message { background: url(../img/message.png) no-repeat; } .large {
background-size: 64px 64px; } .normal { background-size: 32px 32px; }
.small { background-size: 16px 16px; }

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进行配置,而这个属性其实是作用于整个雪碧图上的。

这样做的弊端很明显:

随之而来的解决方案

回想整个渲染执行的流程:

  1. background-size 作用于整个雪碧图,对其尺寸缩放。
  2. background-position 定位。

那么要得到正确的效果的话,以放大两倍为例,需要做到:

  1. 将伪元素的尺寸扩大2倍
CSS

.message:after { width: originWidth * 2; height: originHeight * 2;
}

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6ce3955d4080086130-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6ce3955d4080086130-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6ce3955d4080086130-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6ce3955d4080086130-4">
4
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6ce3955d4080086130-1" class="crayon-line">
.message:after {
</div>
<div id="crayon-5b8f6ce3955d4080086130-2" class="crayon-line crayon-striped-line">
    width: originWidth * 2;
</div>
<div id="crayon-5b8f6ce3955d4080086130-3" class="crayon-line">
    height: originHeight * 2;
</div>
<div id="crayon-5b8f6ce3955d4080086130-4" class="crayon-line crayon-striped-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>
  1. 将整个雪碧图的尺寸扩大2倍
CSS

.message:after { background-size: originSpriteWidth * 2,
originspriteHeight * 2; }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6ce3955d8478968227-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6ce3955d8478968227-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6ce3955d8478968227-3">
3
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6ce3955d8478968227-1" class="crayon-line">
.message:after {
</div>
<div id="crayon-5b8f6ce3955d8478968227-2" class="crayon-line crayon-striped-line">
    background-size: originSpriteWidth * 2, originspriteHeight * 2;
</div>
<div id="crayon-5b8f6ce3955d8478968227-3" class="crayon-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>
  1. 将坐标偏移量相应扩大2倍
CSS

.message:after { background-position: originBackgroundPositionX *
2, originBackgroundPositionY * 2; }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6ce3955db510312506-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6ce3955db510312506-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6ce3955db510312506-3">
3
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6ce3955db510312506-1" class="crayon-line">
.message:after {
</div>
<div id="crayon-5b8f6ce3955db510312506-2" class="crayon-line crayon-striped-line">
    background-position: originBackgroundPositionX * 2, originBackgroundPositionY * 2;
</div>
<div id="crayon-5b8f6ce3955db510312506-3" class="crayon-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

图片 4

图片

综上,如果想在雪碧图内实现缩放逻辑,必须通过

  • 雪碧图长宽
  • 该图标原始长宽
  • 该图标在雪碧图中的偏移量

总共6个变量去实现。用动态样式语言的话,或许可以得到这么一个通用函数:

CSS

/* 雪碧图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) {
… }

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么?会出现同样的问题么?
那么要得到正确的效果的话,以放大两倍为例,需要做到:
恩?好像就加一行代码?

CSS

.message:after { … transform: scale(2); }

1
2
3
4
.message:after {
    …
    transform: scale(2);
}

好吧,就是这样。两者区别也很简单,因为一个作用在元素上,一个作用在雪碧图上,所以后者会带出n多副作用。

那么如果需要缩放到固定尺寸时,还需要知晓原始尺寸,通过计算得到一个缩放系数,这样才能最终达到所需的效果。

如果要使高度能更好的适应各种手机屏幕,需要写太多的媒体查询样式,效率低下

最后的总结

回顾一下,一个简单的缩放需求,出现了三种解决方案:

  1. 最无脑的全部放进雪碧图中
  2. 最冗长的修改background-size
  3. 以及最简短的transform变形

一般情况下,还是无视第二种方案吧。那么在1和3两者中间,则各有取舍。现在回想整个寻求解决方案的过程,个人还是比较倾向方案1的,毕竟,几乎没有什么出错的可能。而且都是同一张雪碧图,并没有更多的请求数,只是多了点图片大小而已。

全屏背景图片跟页面元素需要耦合时,元素位置的确定尤为困难(可能需要通过百分比去确定元素的横向位置,但始终会有误差)

相关参考资料

一种雪碧图自动化方案的设想

MDN:background-size

1 赞 2 收藏
评论

图片 5

最近在微博上看到流云诸葛总结的一篇文章《从网易与淘宝的font-size思考前端设计稿与工作流》,其中介绍到的几种Web
App适配方案,我们现在的做法恰好是跟拉勾网类似的简单方案,当然就会有上面我提到的一些问题,最后经过预研和demo测试,我们采取了网易跟淘宝的方案,其实这两者的方案是大同小异,都是基于rem的适配方案。

2、解决问题的方案

网易跟淘宝的方案介绍在上面流云诸葛的文章中已经写的很清楚了,建议可以先看看那篇文章再阅读下面我所说的,可能会更加清晰。

(1)方案的简单介绍: 基于rem

前提:页面元素的布局尺寸全都以设计稿为基准等比例设置。

给html根节点设置一个基础font-size值,然后页面的所有元素布局均相对于该font-size值采用rem单位设定。那么基础的font-size值该如何取呢?

假如通过媒体查询设置font-size,只能解决一部分的情况,而且并不能完成适配,因为手机屏幕宽度类型实在太多了,所以font-size的取值要通过js计算,取当前viewport的deviceWidth与设计稿的宽

比例值,例如:我们的设计稿尺寸都是640px的,iphone5的deviceWidth是320px,那么计算出来的font-size值就是
320 / 640 =
0.5,因为得出的font-size太小,不方便计算,且有的浏览器可能不兼容太小字号,所以将font-size放大100倍,所以最终计算出来的font-size为
320 / 640 * 100 = 50(px);
当然,这个值是根据设计稿来计算的,所以根据计算规则,下面列出几种常见设计稿相应的font-size值:

deviceWidth = 320,font-size = 320 / 6.4 = 50px

deviceWidth = 375,font-size = 375 / 6.4 = 58.59375px

deviceWidth = 414,font-size = 414 / 6.4 = 64.6875px

deviceWidth = 500,font-size = 500 / 6.4 = 78.125px

可在script标签加上如下代码

(function() {

document.addEventListener(‘DOMContentLoaded’,function() {

varhtml = document.documentElement;

varwindowWidth = html.clientWidth;

html.style.fontSize = windowWidth / 6.4 +’px’;

// 等价于html.style.fontSize = windowWidth / 640 * 100 + ‘px’;

},false);

})();

// 这个6.4就是根据设计稿的横向宽度来确定的,假如你的设计稿是750

// 那么 html.style.fontSize = windowWidth / 7.5 + ‘px’;

至此,font-size的基础值就确定好了,而且知道该font-size值是手机deviceWidth跟设计稿的比例值
的 100倍(重要)

(2)那么页面元素该如何设置宽高、边距

例如:一个设计稿宽高为140px的图标,左边距为50px,那么它的css应该这样写

.icon {

width: 1.4rem;/* 像素换算rem:140px / 100 = 1.4rem */

height: 1.4rem;

margin: 0 0 0 .5rem;

}

因为html的font-size是放大了100倍,所以计算rem时,要用设计稿的实际像素除以100,140px
/ 100 = 1.4rem; 最后实际的像素大小就会由deviceWidth跟设计稿的横向宽 的
比例 自动计算出来。

如图iPhone5下面的效果:

图片 6

iPhone6的效果:

图片 7

可以看出来:html的font-size动态根据deviceWidth改变,图标的宽高、边距等也根据font-size动态按比例变化,大功告成了?不对,相信机智的你已经看到貌似在iPhone6的下有的图标背景错位了。。是的,这暴露出了一个背景使用雪碧图的一个弊端(由于font-size小数点太多,计算出实际背景图大小background-size跟背景图位置background-position时浏览器精度不够可能就会出现位置的偏差(我猜的),这个后面还会详细讲解决方案)

到这里,设置宽高、边距等都OK了,接下来…

(3)其他元素的字体大小该如何设置?

在流云诸葛的文章中讲到,网易跟淘宝的做法都是使用额外的媒体查询设置几种字体大小,例如:

@mediascreen and (max-width: 320px) {

body{font-size: 14px;}

}

@mediascreen and (min-width: 321px) and (max-width: 413px) {

body{font-size: 16px;}

}

@mediascreen and (min-width: 414px) and (max-width: 639px) {

body{font-size: 17px;}

相关文章

Leave a Reply

电子邮件地址不会被公开。 必填项已用*标注