一个标签的72变,打造一个纯CSS图标库
每次要用到图标的时候都会到 icono 去 copypaste,但每次用到的时候尺寸都各不一样,总是要调整参数,巨烦。当然你可以会想到用 zoom、scale 来做缩放,但是这样的缩放会使得线宽也变粗了,不甚满意。
终于下定心思来改造一个可缩放的图标库。github 先上:https://github.com/qieguo2016/iconoo,目前提供下载 link 标签引入和 npm+webpack 的引入方式,详见项目的 readme。(喂,来个 star!)。上图:
关于改造,一开始的想法就是使用百分比尺寸来改造,然后马上发现不可行了,绘制图标最依赖的两种手段:border、box-shadow 都不可以用百分比,所以这个想法,pass!然后很自然就想到了在单位上做文章,rem?No、No、No,一个库依赖全局变量那简直是个笑话。剩下的自然就是 em 了,在 icon 级设置 font-size,然后 icon 本身以及后代都以这个 font-size 为参照,Perfect!
CSS 绘图的原理
使用 CSS 绘制线条,用到的不外乎两个属性:border & box-shadow。而形状则可以用 border-radius、transform 控制,位置会用到绝对定位、transform、margin 等。CSS 的绘图,做过几个就知道大概是怎么回事了,归根到底,还是几何。
比如最简单的加号:
1 .plus { 2 box-sizing : border-box; 3 display : inline-block; 4 position : relative; 5 font-size : 20px; 6 } 7 8 .plus:before, plus:after { 9 content : ''; 10 pointer-events : none; 11 position : absolute; 12 left : 50%; 13 top : 50%; 14 transform : translate(-50%, -50%); 15 box-shadow : inset 0 0 0 1em; 16 } 17 18 .plus:before { 19 width : 1em; 20 height : 2px; 21 } 22 23 .plus:after { 24 height : 1em; 25 width : 2px; 26 }
实现非常简单,通过设置两个伪类的宽高形成横竖两个小矩形,接着用阴影填充满,这样一个加号必需的图形就出来了。然后就是调整位置了,将这两个矩形居中,加号就出来了。具体是通过绝对定位 + 反向偏移的方式,巧妙利用了这两个属性百分比参照的不同实现居中。尺寸方面,所有尺寸除了线宽(2px)外都使用 em 这个相对单位,所以调整 font-size 的值就可以调整图标的大小了。如果要调整线宽,那就需要改变这个 2px 了,引入 less、sass 将线宽定义成变量就可以很方便地改变线宽了。
CSS 的各种玩法
原理虽然简单,但是很多图标还是相当有意思的,通过分析这些图标也能加深对 css 的认识。例如:
这个图形网上说的应该还是比较多的了,第一眼看到懵逼了。。。分析一下,最外层的边框明显可以用 border 来做,然后用个 before 来做圆点也非常简单,关键是两座大山要如何绘制呢?box-shadow 貌似可以做多层边框呢,然后加个旋转是不是就出来了呢?绘制流程如下:
上 CSS 代码吧:
1 .icon-test { 2 display: inline-block; 3 position: relative; 4 box-sizing: border-box; 5 width: 90px; 6 height: 80px; 7 border: 5px solid; 8 border-radius: 10px; 9 color: #2ba5bb; 10 overflow: hidden; 11 } 12 13 .icon-test:before,.icon-test:after { 14 content: ''; 15 pointer-events: none; 16 position: absolute; 17 } 18 19 .icon-test:before { 20 width: 10px; 21 height: 10px; 22 top: 18px; 23 right: 20px; 24 box-shadow: inset 0 0 0 1em; 25 border-radius: 50%; 26 } 27 28 .icon-test:after { 29 width: 60px; 30 height: 50px; 31 left: 0; 32 bottom: -27px; 33 box-shadow: inset 0 0 0 50px,30px -20px 0 0; 34 transform: rotate(45deg); 35 }
再来一个:
看起来跟上一个有点像,然而,按照上一个的绘制方式却怎么也画不出来 ~~~ 还是分解几步来画,边框很容易解决,一个 box-shadow 就完事。这两座大山其实形状都一样,都是一个三角形下接一个矩形,三角形显然可以用 border 来画,而矩形用 box-shadow 就可以了!这里还用了透明 border 来做左侧和下侧的留白,比直接用尺寸对齐要好很多。
1 .icon-test { 2 display: inline-block; 3 position: relative; 4 box-sizing: border-box; 5 color: #2ba5bb; 6 width: 60px; 7 height: 40px; 8 border-top-width: 0; 9 border-right-width: 0; 10 border: 4px solid; 11 border-color: transparent; 12 box-shadow: -4px 5px; 13 overflow: hidden; 14 } 15 16 .icon-test:before,.icon-test:after { 17 content: ''; 18 pointer-events: none; 19 position: absolute; 20 } 21 22 .icon-test:before { 23 left: 0; 24 bottom: 8px; 25 border: 14px solid transparent; 26 border-bottom-color: currentColor; 27 box-shadow: 0 16px; 28 } 29 30 .icon-test:after { 31 left: 28px; 32 bottom: 9px; 33 border-width: 0 9px 21px; 34 border-style: solid; 35 border-color: transparent transparent currentColor; 36 box-shadow: 0 17px; 37 }
怎么样?觉得这些都是小玩意?好吧,都让开,我要开始装逼了!
蒙娜丽莎?什么鬼?我会告诉你这也是一个单标签纯 CSS 画出来的吗?
几千条 box-shadow 构成的蒙娜丽莎,看的我内分泌都失调了。。。
(author:Jay Salvat, 来源:http://codepen.io/jaysalvat/pen/HaqBf )
如此变态的绘图,都没有怎么用到 CSS 中最强大的变形,如果加入变形,那可以画出来的形状就更多了。。。更多 CSS 玩意儿,请到 codepen 上去探宝吧!
PS: 蒙娜丽莎这种图形,可以读取原图信息转换成单位面积的 box-shadow,前端用 canvas 就可以做的,其实这货的技术含量比一个图片图标还要少呢。话虽如此,复杂图形使用 CSS 来绘制的话,性价比还是太低,建议还是使用图片,这样会更具表现力一些,操作起来也更加简单!专业的绘图还是交给专业的 UI 去做吧!
大大小小的坑
其实,遇到的这些都不能叫做坑,是自己对 CSS 的理解度不够而已。原以为,将原来 icono 使用的单位换算成 em 就算完事了,然而,一改 font-size 就变形了,顿时懵逼!究其原因,其实也很简单,并不是所有地方与 font-size 都是正比的,很多地方混入了线宽的影响,所以要剔除线宽的影响。
去除线宽影响的方法不外乎两种:
1)去掉线宽,例如使用 box-shadow 等不影响尺寸的属性
2)将线宽纳入计算内,比如 translate 反向偏移掉线宽,这样整体缩放就不会受到线宽的影响了。
另外一个比较烦的就是居中,其实居中基本上就只用到了下面两种方式,还是蛮简单的。只是,这个反复的 copypaste,烦哪!
1. 绝对定位 +margin:auto。
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
实现原理:利用 css 定位规则,设置左右、上下方向定位为 0,margin 为 auto,让 css 根据定位计算 margin 值,用 hack 的方式实现居中。居中块的尺寸需要可控, 因为 css 计算 margin 时也需要参考尺寸值,由于四周为 0,所以自动计算得到的盒子尺寸 (含 margin) 是与父容器一样的。无论是设置居中块的 width、height 或者是 max-height、max-width,都是让尺寸不会扩大到与父级一样。
局限:在参考系父级 (position!=static) 大小比本身要小的时候,水平方向的居中就会失效。(垂直方向依然居中)
2. 绝对定位 + transform 反向偏移。
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); margin: auto;
实现原理:先绝对定位相对父级偏移 50%,然后使用 transform 来反向偏移。由于 transform 的计算基准是元素本身,所以这里可以用 50% 来做反向偏移。
局限:这个方案需要固定居中块的尺寸值(不能设置 max-width 等范围限制),浏览器需要以此为基准来计算定位!
综合来说,方案 2 的居中方式明显会比方案 1 要好,但是在绘制图标的时候会用到 transform 来做一些偏移,为了不覆盖偏移效果所以要用到方案的方式来做居中。除了这两种居中方式,还有 inline-block 对齐 after/before 子元素的方式,还有 table 和 flexbox 的方式来实现居中,但是画图标本身层级有限而且也用到了 before/after,所以不适用图标绘制。
最后一点
目前纯 CSS 的图标还是挺多应用场景的,这种图标的方案免去了做雪碧图和维护雪碧图的麻烦,而且减少了图片资源的请求,从性能上来说会有那么 0.01s 的提高吧。不用堆雪碧图还方便调整颜色,性能还有 0.01s 的优化,这套 CSS 图标你还不赶紧用起来?!
PS:各位看官觉得不错的话,还请顺手给个 github 星星哈。多攥几颗星星,说不定就升职加薪赢取白富美了呢 ~~~
PPS:再附上几个神级的 CSS 效果吧 (by Fabrizio Bianchi 来源: http://codepen.io/fbrz/pen/iqtlk):