谈谈Web图标

前段时间意图模仿百度开放云的UE风格时,想把标志不同状态的圆点抠出来作为<img>放到自己项目中,结果一查发现他们不是<img>,而是@font-face方式实现,顿感自己的无知,因此有了这篇小结式的文章。

0x00 序

随着前端技术的不断革新,当前Web中的图标(Icons)已经不再仅仅是局限于<img>。除此之外,还有Sprites(俗称雪碧图)、Icon Font(字体图标)、SVG Icon等等。本文就将探讨一下这些方法在Web中实现图标的利弊。

0x01 <img>标签

<img>标签是用来给Web页面添加图片的。而图标(Icons)也算是图片的一种,因此也可以在页面中直接用<img>来加载图标,且可以加载任何适用于Web页面的图标格式,例如:.jpg(或.jpeg)、.png、.gif。对于今天的Web,除了这几种图片格式之外,还可以直接引用.webp和.svg图像(图标)。

对于这种实现方式,其优点乏善可陈(最大的优点就是看起来很好下手),劣势也是明显的。

优势

  • 更换简单方便,只需要修改图标路径或覆盖图标文件名
  • 图标大小易于掌握

劣势

  • 增加HTTP请求数,如果页面使用很多图标,则会直接拉高HTTP请求书,从而影响了页面加载性能。
  • 不利于适配各种终端和分辨率,可能会导致在Retina屏中图标显示模糊(除非加载的是矢量图标.svg,或者一开始就加载了适合高PPI的图标)
  • 不易修改图标的样式,比如颜色,阴影等
  • 不易维护

0x02 CSS Sprites(雪碧图)

由于<img>的局限性与不足,2004年3月Dave Shea提出了CSS Sprites的技术(在国内常常称为CSS雪碧,或者CSS精灵)。

现在有很多页面都有采用这种技术,把页面上的ICON、栏目背景、图片按钮等有规则的合并一张背景图,然后利用background-position的负值来进行调节,实现背景图片的定位。

早期CSS Sprites使用的都是位图,且一般采用的都是.png文件格式。但是在现在的web环境中,若只使用位图,会受到很多的限制,比如在Retina屏下,位图会模糊。也就是说,为了适配各种终端设备分辨,CSS Sprites不在局限于位图,也可以将SVG这样的矢量图集合在一起。其和位图最大的不同之处可以根据设备分辨率,调整Sprites的尺寸,从而不影响图标在设备的呈现质量。

优势

  • 减少HTTP请求数
  • 可以是任意图形,也可以是任意色彩
  • 兼容性极好(对于位图的Sprites兼容性都非常的好,但对于SVG的Sprites,还是受到浏览器的限制,最起码要支持SVG的浏览器才能得到支持)

劣势

  • 增加开发时间,需要人肉或通过相关工具,将零散的图形合并到一起,而不同的合并方式,图形的色彩对Web的性能有直接的影响
  • 增加维护成本,要增加新的图标合成进来,是件较难的事情,甚至直接会影响到前面又定位好的图片。目前为止,使用自动编译工具,相对比人肉处理要理想一些
  • 图片尺寸固定,在位图的Sprites中无法通过CSS来修改图标的大小,但在SVG的Sprites中可以配合CSS的background-size调整图标的大小

0x03 字体图标(Icon Font)

尽管CSS Sprites有其足够的优势,而且很多开发者都在使用这种技术,但是现如今,随着Retina屏的出现,开发者不得不考虑面对各种高清屏幕的显示效果,不得不对CSS Sprites说再见(主要是对位图说再见)。

在这里,我想扯开去说下响应式图片。

响应式图片(Responsive Image)

什么是响应式图片

响应式图片是指:用户代理根据输出设备的分辨率不同加载不同类型的图片,不会造成带宽的浪费。同时,在改变输出设备类型或分辨率时,能及时加载对应类型的图片。

表面上看,这是一件非常简单的事情,只要把图片元素的高、宽属性值都移去,然后设置max-width属性为100%即可。不过这么做的前提是你必须要创建一幅尽可能高分辨率的图片。

在当前响应式设计和自适应设计的流行下,很多web 应用往往都兼容手机、平板和PC,其中一个让人比较头痛的问题就是图片的加载了。不同平台显然不可能用同一张大的图片,这样子不但浪费手机流量、影响网站载入速度并且在小屏幕下会很不清晰。让浏览器根据分辨率自动识别图片是最好的方法。

响应式解决方法

目前可能存在的两种根据屏幕不同而输出不同分辨率的图片的方式:

  1. 可以使用<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>
  2. 创建新元素或属性: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实现Retina屏幕下的图像显示
  • 通过媒体查询,在不同的屏幕下调用不同的图像;当你为高分辨率屏幕适配网页的时候(比如苹果的Retina屏幕),一般会添加更大尺寸的图片资源,并且使用CSS中的Media Query来识别并适配尺寸。但是如此一来,文件数量和大小会急剧增加,并且会增加代码中的CSS选择器的数量,引用更多的文件。
  • 使用img的srcset属性调用不同的图像
  • 使用标签元素根据不同的屏幕调用不同的图像

不管使用哪种方法,都不是一件易事,比如使用image-set和媒体查询,只适合背景图像;而对于srcset和picture方法仅适合Web引入图像的情景。而且这些方法直到目前为止在浏览器上都受到了很多的限制。

为了解决屏幕分辨率对图标影响的问题,字体图标(Icon Font)就顺势而生了。字体图标是一种全新的设计方式,更为重要的是相比位图而言,使用字体图标可以不受限于屏幕分辨率,冲着这一点就具有非常强的优势,而且字体图标还具有一个优势是,只要适合字体相关的CSS属性都适合字体图标,比如说:

  • 自由使用font-size变化图标的大小
  • 自由使用color修改图标颜色
  • 可以添加一些视觉效果,比如:阴影、旋转、透明度
  • 兼容IE6

基于这些原因,现在Web开发中,使用字体来制作图标的应用也越来越多。

通常应用步骤:

  1. 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- */ }
  2. 定义使用iconfont的样式

    .iconfont{font-family:"iconfont"; font-size:16px;font-style:normal;}
  3. 挑选相应图标并获取字体编码,应用于页面

    <i class="iconfont">&#33</i>

优势

  • 减少了HTTP的请求
  • 很容易地缩放、改变颜色、产生阴影、拥有透明效果
  • 浏览器兼容性较好
  • 可以到很CSS很好支持
  • 可以快速转化形态
  • 可以做出跟位图一样可以做的事情
  • 本身体积更小

劣势

  • 它们只能被渲染成单色或CSS3的渐变色
  • 使用限制性很大,除非你想花时间去创作你自己的字体图标
  • 创作字体图标很耗时间
  • 可访问性差
  • 字体文件体积过大,直接影响页面加载性能,特别是加载一个包含数百图标的Fonts,却只使用其中几个图标
  • 在不同的设备浏览器字体的渲染会略有差别,在不同的浏览器或系统中对文字的渲染不同,其显示的位置和大小可能会受到font-size、line-height、word-spacing等CSS属性的影响,而且这种影响调整起来较为困难
  • 为了实现最大程度的浏览器支持,可能要提供至少四种不同类型的字体文件。包括.ttf、.woff、.eot和.svg格式字体
  • 不兼容旧的手机浏览器:Opera mini,Android 2.1,Windows Phone 7.5-7.8
  • 在手机上可能与系统字体冲突

Icon Font的主要缺陷有以下几条:

  1. 浏览器将其视为文字进行抗锯齿优化,有时得到的效果并没有想象中那么锐利。 尤其是在不同系统下对文字进行抗锯齿的算法不同,可能会导致显示效果不同
  2. Icon Font作为一种字体,Icon显示的大小和位置可能要受到font-sizeline-heightword-spacing等等CSS属性的影响。icon所在容器的CSS样式可能对icon的位置产生影响,调整起来很不方便。
  3. 使用上存在不便。首先,加载一个包含数百图标的Icon Font,却只使用其中几个图标,非常浪费加载时间。自己制作Icon Font以及把多个Icon Font中用到的图标整合成一个Font也非常不方便
  4. 为了实现最大程度的浏览器支持,可能要提供至少四种不同类型的字体文件。包括TTF、WOFF、EOT 以及一个使用 SVG 格式定义的字体。

0x04 SVG图标

为了适配各种分辨率,让图标显示更完美,除了字体图标之外,还可以使用SVG图标。SVG图标是一种矢量图标。其实回过头来看,字体图标其实也是使用SVG封装过的。

SVG(Scalable Vector Graphics)是一种古老的技术(不过字体应该更古老),字面理解为可缩放矢量图形,是一种基于xml的图形,所以也可以理解为是开发者通过代码绘制的图形,所以他不受分辨率影响(没有马赛克)。

为什么要使用SVG图标

SVG图标实际上是一个服务于浏览器的XML文件,而不是一个字体或像素的位图。它是由浏览器直接渲染XML,在任何大小之下都会保持图像清晰。而且文件中的XML还提供了很多机会,可以直接在代码中使用动画或者修改颜色,描边等。不需要借助任何图形编辑软件都可以轻松的自定义图像。除此之外,SVG图像也有过字体图标的一个主要优势:拥有多个彩色图像的能力。

Inline SVG

所谓Inline SVG,就是直接把 SVG 写入 HTML 中,这种方法简单直接,而且具有最强的可调性。 使用这种方法,你可以使用 CSS 的fill属性(文字颜色填充)/和stroke属性(文字描边)来控制填充颜色和边线的颜色, 如果 SVG 图标包含多个部分,你甚至可以设置每个部分的样式。同时,使用 JavaScript 修改 SVG 和生成动画效果都可以实现。
Inline SVG 作为 HTML 文档的一部分,不需要单独请求。临时需要修改某个图标的形状也比较方便。 但是 Inline SVG 使用上比较繁琐,需要在页面中插入一大块 SVG 代码因此不适合手写,图标复用起来也比较麻烦。
好在我们大部分的页面都是由某种模板渲染出来的,无论是使用 PHP、Jinja2 还是 ERuby 模板语言, 都可可以定义一个函数来帮我们 include 这些 SVG。因此在很多情况下是很好的解决方案, 其不适合的主要使用场景就是纯静态页面或者前后端分离客户端页面。

优势

  • SVG图标是矢量图形文件,可以随意修改大小,而且不会影响图标质量
  • 可以使用CSS样式来自定义图标颜色,比如颜色、尺寸等效果
  • 所有SVG图标可以全部放在一个SVG的文件中(SVG Sprites),节省HTTP的请求
  • 使用SMIL、CSS或者JavaScript可以制作动画效果
  • 可以使用gzip的方式把文件压缩到很小
  • 可以很精细的控制SVG图标的每一部分

劣势

  • 浏览器兼容性较差
  • 需要学习SVG相关知识
  • 需要了解使用制作软件绘制SVG图形或专业的SVG图形编辑软件

0x05 Data URIs

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相比就没有任何优势了。

0x06 纯CSS实现

除了前面提到的几种图标实现方式以外,一些简单的图标也可以直接用纯CSS实现。主要是通过设置元素为position:relative;,设置:before:afterposition:absolute;,然后通过CSS相关的backgroundborderborder-radiustransform:rotate()、和z-index来交叉生成。

这里推荐一个纯CSS生成图标的库:http://saeedalipoor.github.io/icono/

优势

  • 通常比其他图标体积小很多

劣势

  • 有浏览器兼容性问题
  • 能够实现的图标有限

0x07 如何选择

前面介绍了Web中制作图标的几种常见方案,每种方案都有其自己的利弊。那在实际中要如何选择呢?这需要根据自身所在的环境来做选择:

  • 如果你需要信息更丰富的图片,不仅仅是图标时,可以考虑使用<img>
  • 使用的不是展示类图形,而是装饰性的图形(包括图标),而且这部分图形一般不轻意改变,可以考虑使用PNG Sprites
  • 如果你的图标之类需要更好的适配于高分辨率设备环境之下,可以考虑使用SVG Sprites
  • 如果仅仅是要使用Icon这些小图标,并且对Icon做一些个性化样式,可以考虑使用Icon Font
  • 如果你需要图标更具扩展性,又不希望加载额外的图标,可以考虑在页面中直接使用SVG代码绘制的矢量图

当然,在实际开发中,可能一种方案无法达到你所需的需求,你也可以考虑多种方案结合在一起使用。

0x08 总结

全文主要介绍了Web中图标的几种方案之间的利与弊。相对而言,如果不需要考虑一些低版本用户,就当前这个互联网时代,面对众多终端,较为适合的方案还是使用SVG。不管是通过img直接调用.svg的文件还是使用SVG的Sprites,或者直接在页面中使用SVG(直接代码),都具有较大的优势。不用担心,使用的图标在不同的终端(特别是在Retina屏)会模糊不清。而且SVG还有一个较大的优势,你可以直接在源码中对SVG做修改,特别是可以分别控制图标的不同部分,加入动画等。

当然,你或许会有众多的顾虑,不懂SVG的怎么破,就算不懂SVG,你也可以借助SVG的图形编辑软件或者工具,来协助你。除此之外,除了这些方式在Web中嵌入图标之外,对于一些简单的小图标,可以考虑直接使用CSS代码来编写,这种方式可能较为费时费力,但其具有的优势,我想大家都懂的。

感觉CSS3的潜力就像是鸣人体内的九尾狐妖一样,能量巨大,不可限量。

0x09 参考资料

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

你可能感兴趣的:(Web,前端)