UDN-企业互联网技术人气社区

板块导航

浏览  : 1254
回复  : 0

[干货] SVG图标好用后备难做

[复制链接]
呵呵燕的头像 楼主
发表于 2016-9-20 22:22:59 | 显示全部楼层 |阅读模式
  最近,图标字体用得越来越少了,好像有很多不错的理由都建议不再用图标字体,而改用SVG图形。对于《金融时报》(Financial Times)而言,我们大致同意这个观点, 是该考虑一下过渡方案了。

  浏览器对SVG的支持非常好,94%以上都完全支持SVG 1.1,如果你不用遮罩,这个比例会提高到96%。

  视情况不同,有人可能不准备为剩余的4%准备后备方案。而实际上,很多用户都是依赖图标的。不管你觉得用户体验好不好,很多网站确实在使用图标实现导航、切换菜单、关闭提示或者让用户执行登录/退出等重要操作。

  下面就是Facebook、Twitter和Google Drive使用图标的情况。

9.png


  一般来说,在界面空间有限的时候,用图标可以省掉文字。这时候,用户就更依赖图标了。

  在FT(金融时报),我们的图标是由金融时报统一平台和Web开发工具包Origami提供的,这个平台必须适应全公司的各种应用场景,要好用、可靠、稳定。考虑到FT的用户都是一些大型企业和老板1,这就意味着必须给那4%看不到SVG的用户提供后备。

  于是,难题来了。

  标记

  实现SVG图标的方法很多,比如行内元素、图标或背景。说到提供后备,其实结果显而易见。

  使用行内元素加后备太过复杂。元素依赖的srcset属性没有被IE、Opera Mini以及Android系支持。后备图片会导致每个图片都发送一次请求。✘

  背景图片早在2000年初就用来实现雪碧图,而从图标字体升级不会改动太多代码。✓

  过去,基于图标的Web字体实现都是添加一个基类,然后再为必要的符号添加特殊类名。字体可以继承颜色和大小,而基于雪碧图的背景不能继承颜色和大小,必须使用额外的类来指定颜色和大小:

  1. <!-- 当前基于字体的实现 -->
  2. <span class="icon icon--{symbol}"></span>

  3. <!-- 建议未来的实现 -->
  4. <span class="icon icon--{size} icon--{colour} icon--{symbol}"></span>
复制代码


  雪碧图

  既然后备图片这么重要,使用SVG片段标识符也不行,那我必须按照2004年的做法,将图标按常规、不相互重叠的形式布局。

  我试过的所有SVG雪碧图工具都可以输出一张一种颜色的雪碧图,要么照搬来源,要么重写。我真不想为我们20多种颜色搭配分别生成不同的文件,因此必须改进我们自己的系统。

  FT有很多符号以及前面提到的大调色板,如果每个网站的用户都要下载一个巨大的雪碧图,结果却只用到其中几个图标,那对这些用户肯定不公平。我曾经做过一个小型应用的原型,用于只生成用到的符号及颜色的雪碧图。

  这个应用从SVG源文件中找到路径,经过优化按定义把结果包含在输出中。每个定义都会计算viewbox,因此它们占的地方一样大。定义没有可见的界面,因此程序会循环一遍请求的颜色,引用相应的路径,再为它们应用相应的填充。

8.png


  这个SVG雪碧图生成器抓到每一个SVG源文件,优化并计算一个viewbox,以便它们占的地方一样大。程序循环所有请求的颜色并引用相应的路径,然后进行填充。

  目前我们只使用系统来生成静态文件,包括SVG和PNG。不过,今后如果有需求,我们还可以再实现一个类似的工具,为我们实时生成雪碧图。

  样式

  以下是最初的Sass代码。别急着嗤之以鼻,容我稍后解释。首先定义了图标大小、颜色和符号。

  使用Sass写会更清晰地展示计算过程,为了清晰起见,稍有改动。

 
  1.  $icon-sizes: (16, 24, 36, 48);

  2.   $icon-colors: ('black', 'white', 'blue', 'red');

  3.   $icon-symbols: ( 'chronometer', 'cog', 'hearts', 'rocket', 'sign', 'speech', 'user');

  4.   .icon {

  5.   position: relative;

  6.   display: inline-block;

  7.   overflow: hidden;

  8.   &:after {

  9.   content: '';

  10.   position: absolute;

  11.   width: percentage(length($icon-colors));

  12.   height: percentage(length($icon-symbols));

  13.   background-image: url('sprite-sheet.svg');

  14.   -webkit-background-size: 100%;

  15.   background-size: 100%;

  16.   }

  17.   }
复制代码


  这里没有给.icon元素应用雪碧图作为背景,反倒引入了一个伪元素,觉得奇怪吗?原因是雪碧图的横轴从左到右依次是各种颜色,纵轴从上到下是相应的符号,而background-position属性无法一个符号一个符号地指定坐标。

  只使用背景定位,需要为每个一个图标和颜色的可能组合定义样式,对FT的50个符号、20种颜色来说,那就是1000个声明。

  而使用绝对定位的伪元素不仅所有浏览器都支持,而且可以使用单独的类分别指定left和right属性:.icon--{colour}用于横轴,.icon--{symbol}用于纵轴。

  相对来说,background-position-x和background-position-y属性是有某些浏览器支持,但它们并不属于CSS规范,而且没有得到所有浏览器支持。已经有人建议将这两个属性已放到背景与边框模块4级了。

  如果应用背景图片的话,还是需要进行绽放才能让每个图片按照包含元素的大小填充。因为雪碧图由普通的大小一样的正方形组成,颜色和符号数量已知,因此可以计算。只要用100%(元素宽度)乘以颜色/符号的数量即可。不过,不是使用这些值去设置background-size属性,而是用它们去设置伪元素以及雪碧图的大小。

7.png


  雪碧图由普通的大小一样的正方形组成,颜色和符号数量已知,因此可以计算。

  初始的设置就是这样,但光有这些还不行。需要不同的类分别指定大小、颜色和符号。在通过列表定义这些参数后,可以迭代它们分别生成不同的声明:

  1.   @each $size in $icon-sizes {

  2.   .icon--#{$size} {

  3.   width: $size + px;

  4.   height: $size + px;

  5.   }

  6.   }

  7.   @each $color in $icon-colors {

  8.   .icon--#{$color} {

  9.   &:after {

  10.   $color-index: index($icon-colors, $color);

  11.   left: percentage($color-index - 1) * -1;

  12.   }

  13.   }

  14.   }

  15.   @each $symbol in $icon-symbols {

  16.   .icon--#{$symbol} {

  17.   &:after {

  18.   $symbol-index: index($icon-symbols, $symbol);

  19.   top: percentage($symbol-index - 1) * -1;

  20.   }

  21.   }

  22.   }
复制代码


  在支持SVG的浏览器中,这样就行了!看这个示例

  现在解决难题……

  后备

  不支持SVG的浏览器大致可分为两类:老版本IE(< IE9)和Android Froyo、Gingerbread等版本中的浏览器。这些浏览器都需要栅格图作为后备,而且不是指定两张背景图那么简单,还必须阻止它们下载和应用SVG文件。

  可以使用不可见的渐变技巧作为SVG支持的代理。不完美(IE9及Android 3中确实支持SVG的浏览器会被排除在外),但还是可以接受的:

  1.   .icon:after {

  2.   background-image: url('sprite-sheet.png');

  3.   background-image: -webkit-linear-gradient(transparent, transparent),

  4.   url('sprite-sheet.svg');

  5.   background-image: linear-gradient(transparent, transparent),

  6.   url('sprite-sheet.svg');

  7.   }
复制代码


  解决了老版本Android中的浏览器问题之后,接下来是老版本IE及其他不支持CSS3背景属性的浏览器。

  一个方案,就是像颜色一样,为每一种大小都提供一个单独的雪碧图。的确可以通过图片服务预生成并提供雪碧图,但这就意味着会有额外的请求,而且只针对需要它们的浏览器也会导致问题。除非你真的能容忍这些问题,否则这个方案还是不行。

  老版本IE中有ActiveX滤镜,其中AlphaImageLoader有一个sizingMethod选项,可以模拟background-size: 100% 100%。遗憾的是滤镜不能应用给伪元素,于是这个奇妙的方案也不能用。

  最终解决问题,用的是另一个非标准属性:zoom。因为必须给图标元素一个尺寸,而且雪碧图已经按已知大小生成了,这里的比例是可以计算的。虽然不能给图标元素指定相对大小,但相信这个问题很好解决。

  让人生气的是,元素的绝对位置也必须根据雪碧图的原始大小重新计算,因为这些属性也是放大的。

  zoom虽然也不属于CSS规范,但它已经得到了相当广泛的支持。我们的后备方案在最新版Chrome中的效果和在IE8中一样。

  我们把每个要重写的后备属性都包装在了相应的媒体查询中。媒体查询与CSS3背景选项放在一块完全没有问题。

  1.   $icon-intrinsic-size: 50;

  2.   @each $size in $icon-sizes {

  3.   .icon--#{$size} {

  4.   width: $size + px;

  5.   height: $size + px;

  6.   &:after {

  7.   zoom: 1 / $icon-intrinsic-size * $size;

  8.   @media all and (min-width: 0) {

  9.   zoom: 1;

  10.   }

  11.   }

  12.   }

  13.   }

  14.   @each $color in $icon-colors {

  15.   .icon--#{$color} {

  16.   &:after {

  17.   $color-index: index($icon-colors, $color);

  18.   left: $icon-intrinsic-size * ($color-index - 1) * -1 + px;

  19.   @media all and (min-width: 0) {

  20.   left: percentage($color-index - 1) * -1;

  21.   }

  22.   }

  23.   }

  24.   }

  25.   @each $symbol in $icon-symbols {

  26.   .icon--#{$symbol} {

  27.   &:after {

  28.   $symbol-index: index($icon-symbols, $symbol);

  29.   top: $icon-intrinsic-size * ($symbol-index - 1) * -1 + px;

  30.   @media all and (min-width: 0) {

  31.   top: percentage($symbol-index - 1) * -1;

  32.   }

  33.   }

  34.   }

  35.   }
复制代码


  最后的话

  想必大多数看到这篇文章的朋友都不会碰上与FT Origami团队面临的同样的问题。如果你不需要非SVG的后备,或者你的图标只需要显示为一种大小,那么实现SVG雪碧图跟实现图标字体一样很简单。但我们由于面临如此严苛的要求……目前已经发布了一个beta版本的实现,希望得到大家尽量多的反馈,我会及时更新。我愿意回答如下一些问题:

  提供一张高分辨率的PNG是不是更好?

  在一个页面中使用多个SVG图标会不会影响性能?

  克服图标字体的不足比实现SVG图标(以及适当后备)还要难吗?

  在CodePen上查看源代码

  大公司IT设备的升级是一件很复杂的事,因为要花不少钱,所以流程很慢。这不是用户的问题!↩

原文作者: 李松峰 来源:开发者头条

相关帖子

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部