前段时间意图模仿百度开放云的UE风格时,想把标志不同状态的圆点抠出来作为<img>
放到自己项目中,结果一查发现他们不是<img>
,而是@font-face
方式实现,顿感自己的无知,因此有了这篇小结式的文章。
随着前端技术的不断革新,当前Web中的图标(Icons)已经不再仅仅是局限于<img>
。除此之外,还有Sprites(俗称雪碧图)、Icon Font(字体图标)、SVG Icon等等。本文就将探讨一下这些方法在Web中实现图标的利弊。
<img>
标签<img>
标签是用来给Web页面添加图片的。而图标(Icons)也算是图片的一种,因此也可以在页面中直接用<img>
来加载图标,且可以加载任何适用于Web页面的图标格式,例如:.jpg(或.jpeg)、.png、.gif。对于今天的Web,除了这几种图片格式之外,还可以直接引用.webp和.svg图像(图标)。
对于这种实现方式,其优点乏善可陈(最大的优点就是看起来很好下手),劣势也是明显的。
由于<img>
的局限性与不足,2004年3月Dave Shea提出了CSS Sprites的技术(在国内常常称为CSS雪碧,或者CSS精灵)。
现在有很多页面都有采用这种技术,把页面上的ICON、栏目背景、图片按钮等有规则的合并一张背景图,然后利用background-position的负值来进行调节,实现背景图片的定位。
早期CSS Sprites使用的都是位图,且一般采用的都是.png文件格式。但是在现在的web环境中,若只使用位图,会受到很多的限制,比如在Retina屏下,位图会模糊。也就是说,为了适配各种终端设备分辨,CSS Sprites不在局限于位图,也可以将SVG这样的矢量图集合在一起。其和位图最大的不同之处可以根据设备分辨率,调整Sprites的尺寸,从而不影响图标在设备的呈现质量。
尽管CSS Sprites有其足够的优势,而且很多开发者都在使用这种技术,但是现如今,随着Retina屏的出现,开发者不得不考虑面对各种高清屏幕的显示效果,不得不对CSS Sprites说再见(主要是对位图说再见)。
在这里,我想扯开去说下响应式图片。
响应式图片是指:用户代理根据输出设备的分辨率不同加载不同类型的图片,不会造成带宽的浪费。同时,在改变输出设备类型或分辨率时,能及时加载对应类型的图片。
表面上看,这是一件非常简单的事情,只要把图片元素的高、宽属性值都移去,然后设置max-width属性为100%即可。不过这么做的前提是你必须要创建一幅尽可能高分辨率的图片。
在当前响应式设计和自适应设计的流行下,很多web 应用往往都兼容手机、平板和PC,其中一个让人比较头痛的问题就是图片的加载了。不同平台显然不可能用同一张大的图片,这样子不但浪费手机流量、影响网站载入速度并且在小屏幕下会很不清晰。让浏览器根据分辨率自动识别图片是最好的方法。
目前可能存在的两种根据屏幕不同而输出不同分辨率的图片的方式:
可以使用<img>
的srcset
属性调用不同的图像。srcset属性包含一系列的逗号分隔值,一方面是指定图像的url,另一方面指图像将要显示的条件。图像条件我们可以看到有像素密度、视窗尺寸大小或者同时两者都存在。可以说这个属性的基本特征就是:根据指定的条件来做选择,使其工作。
1)若图片是固定大小(pixels),但是想要适应不同分辨率的屏幕,基本语法如下:
<img alt="description" width="320" height="213" src="photo.jpg" srcset="photo-2x.jpg 2x, photo-3x.jpg 3x">
src="photo.jpg"
对于不支持srcset
属性的浏览器以及设备像素比为1x的,将显示这张图片srcset
属性中的每项都是<url> <density>x
的形式, 例如photo-2x.jpg 2x
;不同项的顺序无所谓width
或者height
,那么浏览器会按照图片原本的宽和高来除以设备像素比,例如3x资源被选中时,将渲染50%该图片的宽和高。2)在一些情况下,srcset
还可以根据屏幕的视窗尺寸的宽度来选择图片,这就相当于媒体查询查到一个断点时加载背景图像。通过srcset
,浏览器可以知道可用的图片资源和宽度,通过sizes
,可以知道对于给定的窗口宽度,应当展现什么宽度的<img>
,从而可以加载最佳资源。也就是说,我们无需指定设备像素比,浏览器能自己搞定。
<img alt="description" src="photo-689.jpg" srcset="photo-689.jpg 689w, photo-1378.jpg 1378w, photo-500.jpg 500w, photo-1000.jpg 1000w" sizes="(min-width: 1066px) 689px, (min-width: 800px) calc(75vw - 137px), (min-width: 530px) calc(100vw - 96px), 100vw">
若窗口宽度大于1066px,那么<img>
将会是689px。在一个1x的设备上,浏览器将下载photo-689.jpg,而在一个2x设备上,则会下载photo-1378.jpg图片。
srcset
中每一项都是<url> <width-descriptor>w
的形式,例如photo-689.jpg 689w
。sizes
中每一项都是<media-condition> <image-element-width>
的形式,最后一项是<image-element-width>
创建新元素或属性:web开发者提议创建一个新的picture元素(类似HMTL5中的video这样的元素),该元素中包含其他的图片源,可以根据不同的屏幕调用不同的图像。示例代码如下:
<picture alt="description">
<source src="small.jpg">
<source src="medium.jpg" media="(min-width: 400px)">
<source src="large.jpg" media="(min-width: 800px)">
</picture>
其中的small.jpg是默认情况下显示的图片源,在后面两个source元素则是在特定媒体查询(media queries)条件下显示的图片——这也是开发者所喜欢的一种解决方案。
对于当前一些根据屏幕的不同来输出不同分辨率的图片的手段:
不管使用哪种方法,都不是一件易事,比如使用image-set和媒体查询,只适合背景图像;而对于srcset和picture方法仅适合Web引入图像的情景。而且这些方法直到目前为止在浏览器上都受到了很多的限制。
为了解决屏幕分辨率对图标影响的问题,字体图标(Icon Font)就顺势而生了。字体图标是一种全新的设计方式,更为重要的是相比位图而言,使用字体图标可以不受限于屏幕分辨率,冲着这一点就具有非常强的优势,而且字体图标还具有一个优势是,只要适合字体相关的CSS属性都适合字体图标,比如说:
基于这些原因,现在Web开发中,使用字体来制作图标的应用也越来越多。
通常应用步骤:
font-face声明字体
@font-face {font-family: 'iconfont'; src: url('iconfont.eot'); /* IE9*/ src: url('iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('iconfont.woff') format('woff'), /* chrome、firefox */ url('iconfont.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/ url('iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */ }
定义使用iconfont的样式
.iconfont{font-family:"iconfont"; font-size:16px;font-style:normal;}
挑选相应图标并获取字体编码,应用于页面
<i class="iconfont">!</i>
Icon Font的主要缺陷有以下几条:
font-size
、line-height
、word-spacing
等等CSS属性的影响。icon所在容器的CSS样式可能对icon的位置产生影响,调整起来很不方便。为了适配各种分辨率,让图标显示更完美,除了字体图标之外,还可以使用SVG图标。SVG图标是一种矢量图标。其实回过头来看,字体图标其实也是使用SVG封装过的。
SVG(Scalable Vector Graphics)是一种古老的技术(不过字体应该更古老),字面理解为可缩放矢量图形,是一种基于xml的图形,所以也可以理解为是开发者通过代码绘制的图形,所以他不受分辨率影响(没有马赛克)。
SVG图标实际上是一个服务于浏览器的XML文件,而不是一个字体或像素的位图。它是由浏览器直接渲染XML,在任何大小之下都会保持图像清晰。而且文件中的XML还提供了很多机会,可以直接在代码中使用动画或者修改颜色,描边等。不需要借助任何图形编辑软件都可以轻松的自定义图像。除此之外,SVG图像也有过字体图标的一个主要优势:拥有多个彩色图像的能力。
所谓Inline SVG,就是直接把 SVG 写入 HTML 中,这种方法简单直接,而且具有最强的可调性。 使用这种方法,你可以使用 CSS 的fill属性(文字颜色填充)/和stroke属性(文字描边)来控制填充颜色和边线的颜色, 如果 SVG 图标包含多个部分,你甚至可以设置每个部分的样式。同时,使用 JavaScript 修改 SVG 和生成动画效果都可以实现。
Inline SVG 作为 HTML 文档的一部分,不需要单独请求。临时需要修改某个图标的形状也比较方便。 但是 Inline SVG 使用上比较繁琐,需要在页面中插入一大块 SVG 代码因此不适合手写,图标复用起来也比较麻烦。
好在我们大部分的页面都是由某种模板渲染出来的,无论是使用 PHP、Jinja2 还是 ERuby 模板语言, 都可可以定义一个函数来帮我们 include 这些 SVG。因此在很多情况下是很好的解决方案, 其不适合的主要使用场景就是纯静态页面或者前后端分离客户端页面。
Data URIs是一种不太常见的方式。之前我们在CSS里定义元素的背景图片时,常使用像下面这种方式:
.icon { backgound-image: url(icons/a.png) /* ... */ }
而现在,url当中可以放置的可以不仅仅是指向资源的URL链接,而可以是数据本身。使用Data URIs,无论是图片还是SVG,都可以将其编码为base64并直接写入CSS。例如:
.icon{ background: url(data:text/svg+xml;base64,<base64 encoded data>) }
Data URI 的格式定义如下:
data:[<mime type>][;charset=<charset>][;base64],<encoded data>
使用这种方法,SVG Icon使用起来和Icon Font一样只需要为元素添加CSS即可,所有的资源都可以整合在一个CSS文件中,不需要额外引用SVG文件。这个任务只有简单的字符串和编码处理,基本不需要依赖非JavaScript的库和资源。
但是Data URIs的劣势也是明显的,每次都需要解码,这会阻塞CSS渲染;虽然可以通过分离出一个专用的CSS文件,但是这就需要增加一个请求,那和CSS Sprites、Icon Font和SVG相比就没有任何优势了。
除了前面提到的几种图标实现方式以外,一些简单的图标也可以直接用纯CSS实现。主要是通过设置元素为position:relative;
,设置:before
和:after
为position:absolute;
,然后通过CSS相关的background
、border
、border-radius
、transform:rotate()
、和z-index
来交叉生成。
这里推荐一个纯CSS生成图标的库:http://saeedalipoor.github.io/icono/
前面介绍了Web中制作图标的几种常见方案,每种方案都有其自己的利弊。那在实际中要如何选择呢?这需要根据自身所在的环境来做选择:
<img>
当然,在实际开发中,可能一种方案无法达到你所需的需求,你也可以考虑多种方案结合在一起使用。
全文主要介绍了Web中图标的几种方案之间的利与弊。相对而言,如果不需要考虑一些低版本用户,就当前这个互联网时代,面对众多终端,较为适合的方案还是使用SVG。不管是通过img直接调用.svg的文件还是使用SVG的Sprites,或者直接在页面中使用SVG(直接代码),都具有较大的优势。不用担心,使用的图标在不同的终端(特别是在Retina屏)会模糊不清。而且SVG还有一个较大的优势,你可以直接在源码中对SVG做修改,特别是可以分别控制图标的不同部分,加入动画等。
当然,你或许会有众多的顾虑,不懂SVG的怎么破,就算不懂SVG,你也可以借助SVG的图形编辑软件或者工具,来协助你。除此之外,除了这些方式在Web中嵌入图标之外,对于一些简单的小图标,可以考虑直接使用CSS代码来编写,这种方式可能较为费时费力,但其具有的优势,我想大家都懂的。
感觉CSS3的潜力就像是鸣人体内的九尾狐妖一样,能量巨大,不可限量。
https://drafts.csswg.org/mediaqueries-3/
Inline SVG vs Icon Fonts,翻译版在这里
查看css属性的浏览器兼容性:http://caniuse.com/#search=srcset
https://jakearchibald.com/2015/anatomy-of-responsive-images/
http://www.w3cplus.com/responsive/responsive-images-part-1-using-srcset.html
https://css-tricks.com/which-responsive-images-solution-should-you-use/
http://iconfont.cn/help/platform.html