图标字体的使用在慢慢减少。最近很多人找出了不要使用字体图标的理由,并劝大家使用SVG图像。在《金融时报》(注:作者是一名《金融时报》的前端工程师),我们一致认为,总的来说,是时候探索作出一些转变了。
SVG的浏览器支持很好;超过94%的浏览器完全兼容SVG 1.1规范,如果你不需要使用mask
的话,这个比例可以提高到96%。
根据具体情况,你可以权衡一下,是否为剩下的4%的浏览器提供降级,但是要考虑到对于你的网站访客来说,能看到图标是非常重要的。关于糟糕的用户体验的讨论,很多网站都依赖于使用图标来导航、切换重要的菜单、关闭提示或执行重要的操作日志等。
在空间受限的地方,经常会使用图标。用户必须依靠这些来执行关键操作。
《金融时报》的图标集是Origami的一部分——《金融时报》的品牌标志,以及Web开发工具——必须支持公司的广泛使用,而且使用简单并提供可靠的、健壮的支持。因为《金融时报》有非常多的用户量,所以针对这4%的不支持SVG的降级就尤为重要。
事实证明,降级做起来并没有想象中那么容易。
模板
实现SVG图标有很多种方法——内联元素,图像或背景图像。因为需要提供一个坚实的降级,所以选择如下:
内联
<svg>
元素的降级太复杂。一个普通的<img>
元素需要依赖srcset
,IE/Opera Mini/旧版安卓系统的浏览器不支持。这样每个图标的降级都需要一次单独的请求。✘背景图像从2000年初就在使用了,用来实现sprite和升级字体图标的项目,这样可以不需要全盘改写代码。✓
旧版基于图标的网页字体是根据符号需要,通过添加一个基本class
和变量class
来实现。作为字体它将继承父元素的颜色和尺寸。基于sprite的背景图像方法不能继承,所以需要额外定义其颜色和尺寸:
<!-- current font based implementation 当前字体的基本实现方法 --> <span class="icon icon--{symbol}"></span> <!-- proposed future implementation 将来可能的趋势 --> <span class="icon icon--{size} icon--{colour} icon--{symbol}"></span>
Sprite表
降级图像非常重要,使用SVG片段标识符不是一个选择。我需要给每个图标一个常规的、不重叠的布局,就像我们在2004年做的那样。
每个我试过的SVG sprite工具可以输出一个sprite表,统一颜色,无论是从源逐字复制,还是引入一个设置来覆盖它。我真的很想避免针对20+
个调色板,每种颜色都生成一个单独的文件,所以我们必须推出自己的系统。
在《金融时报》我们有很多符号和前面提到的大调色板,所以我们认为强制每个网站用户都下载一个大sprite sheet是不公平的,如果这个产品只使用几个图标。我写了一个小应用程序,可以生成一张sprite sheet,只包含请求的符号和颜色。
这个应用程序是通过提取源SVG文件的路径,进行一些优化,然后把它们作为定义包含在输出文件中。每个定义都有一个viewBox计算,这样它们占据的空间是相等的。定义对它们的产品本身不可见,所以程序遍历请求的颜色,引用每条路径,然后应用正确的填充。
SVG sprite生成器把路径输出到单独的源SVG文件,优化并计算viewBox,以使它们占据相等的空间。程序遍历请求的颜色和引用每条路径,并应用填充。
现在我们只使用系统来生成静态文件——包括SVG和PNG——但是我们日后可能实现一些类似的,如即时sprite sheet生成,如果我们看到这方面的需求。
样式
这是最初的Sass代码。你可能会有点疑惑,我后面再解释。颜色和符号都被包含字sprite sheet中,连同显示的尺寸变量。
实例都是使用Sass来编写的,为了更清楚地展示所需的计算,已经调整为清晰可观看的状态。
$icon-sizes: (16, 24, 36, 48); $icon-colors: ('black', 'white', 'blue', 'red'); $icon-symbols: ( 'chronometer', 'cog', 'hearts', 'rocket', 'sign', 'speech', 'user'); .icon { position: relative; display: inline-block; overflow: hidden; &:after { content: ''; position: absolute; width: percentage(length($icon-colors)); height: percentage(length($icon-symbols)); background-image: url('sprite-sheet.svg'); -webkit-background-size: 100%; background-size: 100%; } }
我没有使用sprite sheet作为.icon
元素的背景图像可能有点奇怪,而是增加伪元素的复杂性。这是因为sprite sheet使用从左到右带颜色的坐标轴,而符号是从上到下,而background-position
不能单独指定一个坐标轴。
单独使用背景定位需要为每一个图标+颜色组合定义单独的样式——这是一个额外的针对《金融时报》的50个符号和20种颜色的1000条声明。
对伪元素使用绝对定位,在所有浏览器中都是可以工作的。而left
和right
属性可以指定单独的类名;icon--{colour}
对于水平轴,.icon--{symbol}
对于垂直方向。
background-position-x
和background-position-y
属性在浏览器中同样是支持的,但是它们目前不是CSS规范的一部分,所以没有普遍实现。属性已经提出在CSS 4的background
和border
模块中加入。
如果应用背景图像是可行的,但仍然需要进行缩放,这样每个图标才可以填充容器元素,而不管其本身的大小。sprite sheet分为常规、同样大小的正方形。sprite sheet中的颜色和符号数量都了解了所以缩放是可计算的——一般是100%(元素的宽)x 颜色/符号的数量
。然而,不是使用这些值来设置background-size
,它们用来设置伪元素的大小,这样sprite sheet可以设置为合适的尺寸。
sprite sheet分为常规,相同尺寸的正方形,sprite sheet中颜色和符号的数量已知,这样才可以计算缩放。
所以,设置部分完成了,但对于自身不是非常有用。需要Class来指定大小、颜色、要展示的符号。这些参数定义在列表中,可以被遍历来自动生成每个声明:
@each $size in $icon-sizes { .icon--#{$size} { width: $size + px; height: $size + px; } } @each $color in $icon-colors { .icon--#{$color} { &:after { $color-index: index($icon-colors, $color); left: percentage($color-index - 1) * -1; } } } @each $symbol in $icon-symbols { .icon--#{$symbol} { &:after { $symbol-index: index($icon-symbols, $symbol); top: percentage($symbol-index - 1) * -1; } } }
对于支持SVG的浏览器,到这里就完成了!你可以查看codepen上的demo。
现在麻烦的部分到了....
降级
不支持SVG的浏览器大致可以分为两组:旧IE(<IE 9),以及安卓旧浏览器。所有这些浏览器都需要一个栅格图像降级,这可不是简单地指定两个背景图像,它们必须阻止下载以及尝试应用SVG文件。
这是通过不可见的渐变方法来代替SVG支持实现的。它并不完美(IE9和Android 3的旧浏览器不支持SVG,会被淘汰的),但也算好的了:
.icon:after { background-image: url('sprite-sheet.png'); background-image: -webkit-linear-gradient(transparent, transparent), url('sprite-sheet.svg'); background-image: linear-gradient(transparent, transparent), url('sprite-sheet.svg'); }
旧的Android浏览器工作没问题,是时候解决旧版IE和其它不支持CSS3背景选项的浏览器的问题了。
选择一——颜色——为每个尺寸变量提供一个单独的sprite sheet。可以提前生成或根据需求提供sprite sheet,通过一个图像服务,但这种技术也意味着额外的请求,也会出现问题,只针对需要它的浏览器。这个选择只有在你愿意忍受这些缺点的时候才是可行的。
在旧版IE中有ActiveX过滤器,AlphaImageLoader
有一个sizingMethod
选项,可以用来模拟background-size: 100% 100%
。不幸的是过滤器不能应用在伪元素上,如此古怪,所以hack这时候也没用了。
我使用另一个非标准的属性解决了这个问题,zoom
。因为icon
元素必须给定尺寸,sprite sheet才可以按照已知的尺寸和比率生成。这意味着icon
元素不能给定一个相对尺寸,但是我想这是对周围工作的一个小警告。
有点麻烦的是,元素的绝对定位必须按照sprite sheet的原生尺寸重新计算,这些属性也会被放大。
zoom
可能不是CSS规范的一部分但是它已经实现得相当广泛了。最新版本的Chrome中的降级和IE8中的一样。
每个降级属性都被重写,通过包装在媒体查询中的降级。带有CSS3背景选项的媒体查询的支持是非常安全的。
$icon-intrinsic-size: 50; @each $size in $icon-sizes { .icon--#{$size} { width: $size + px; height: $size + px; &:after { zoom: 1 / $icon-intrinsic-size * $size; @media all and (min-width: 0) { zoom: 1; } } } } @each $color in $icon-colors { .icon--#{$color} { &:after { $color-index: index($icon-colors, $color); left: $icon-intrinsic-size * ($color-index - 1) * -1 + px; @media all and (min-width: 0) { left: percentage($color-index - 1) * -1; } } } } @each $symbol in $icon-symbols { .icon--#{$symbol} { &:after { $symbol-index: index($icon-symbols, $symbol); top: $icon-intrinsic-size * ($symbol-index - 1) * -1 + px; @media all and (min-width: 0) { top: percentage($symbol-index - 1) * -1; } } } }
总结
我想你们大多数人不会面临像《金融时报》的Origami团队这样的需求。如果你不需要一个非SVG的降级,或你的图标尺寸都是单独的,那么SVG sprite是最直接的实现图标字体的方法。但是,考虑到如此严格的要求,我们已经推出了beta实现,希望收集尽可能多的反馈,我也会适时地在这里更新。我想回答的问题是:
它会比提供一个高分辨率的PNG更高效吗?
在一个页面上使用很多SVG图标是否会有性能问题?
字体图标的缺点是否大于实现SVG图标的复杂度(包括适当的降级)?
最后效果如下:
网友评论文明上网理性发言 已有0人参与
发表评论: