一个标签的72变,打造一个纯CSS图标库

zhyaof 7年前
   <p>每次要用到图标的时候都会到 <strong>icono</strong> 去copypaste,但每次用到的时候尺寸都各不一样,总是要调整参数,巨烦。当然你可以会想到用zoom、scale来做缩放,但是这样的缩放会使得线宽也变粗了,不甚满意。</p>    <p>终于下定心思来改造一个可缩放的图标库。<em><strong> </strong></em>目前提供下载link标签引入和npm+webpack的引入方式,详见项目的readme。 </p>    <p>关于改造,一开始的想法就是使用百分比尺寸来改造,然后马上发现不可行了,绘制图标最依赖的两种手段:border、box-shadow都不可以用百分比,所以这个想法,pass! 然后很自然就想到了在单位上做文章,rem?No、No、No,一个库依赖全局变量那简直是个笑话。剩下的自然就是em了,在icon级设置font-size,然后icon本身以及后代都以这个font-size为参照,Perfect!</p>    <h3><strong>CSS绘图的原理</strong></h3>    <p>使用CSS绘制线条,用到的不外乎两个属性:border & box-shadow。而形状则可以用border-radius、transform控制,位置会用到绝对定位、transform、margin等。CSS的绘图,做过几个就知道大概是怎么回事了,归根到底,还是几何。</p>    <p>比如最简单的加号:</p>    <pre>  <code class="language-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 }    View Code</code></pre>    <p>实现非常简单,通过设置两个伪类的宽高形成横竖两个小矩形,接着用阴影填充满,这样一个加号必需的图形就出来了。然后就是调整位置了,将这两个矩形居中,加号就出来了。具体是通过绝对定位+反向偏移的方式,巧妙利用了这两个属性百分比参照的不同实现居中。尺寸方面,所有尺寸除了线宽(2px)外都使用em这个相对单位,所以调整font-size的值就可以调整图标的大小了。如果要调整线宽,那就需要改变这个2px了,引入less、sass将线宽定义成变量就可以很方便地改变线宽了。</p>    <h3><strong>CSS的各种玩法</strong></h3>    <p>原理虽然简单,但是很多图标还是相当有意思的,通过分析这些图标也能加深对css的认识。例如:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/2c3202e9ad9324f2840ae9f4aff447c9.png"></p>    <p>这个图形网上说的应该还是比较多的了,第一眼看到懵逼了。。。分析一下,最外层的边框明显可以用border来做,然后用个before来做圆点也非常简单,关键是两座大山要如何绘制呢?box-shadow貌似可以做多层边框呢,然后加个旋转是不是就出来了呢?绘制流程如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1277bcd41cb53a8d5873fcdad528480d.png"></p>    <p>上CSS代码吧:</p>    <pre>  <code class="language-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 }    View Code</code></pre>    <p>再来一个:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d47a52ecaf0d88cc6c95ec451a2e5162.png"></p>    <p>看起来跟上一个有点像,然而,按照上一个的绘制方式却怎么也画不出来~~~还是分解几步来画,边框很容易解决,一个box-shadow就完事。这两座大山其实形状都一样,都是一个三角形下接一个矩形,三角形显然可以用border来画,而矩形用box-shadow就可以了!这里还用了透明border来做左侧和下侧的留白,比直接用尺寸对齐要好很多。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9fd39394c686082b91e31868cc30265a.png"></p>    <pre>  <code class="language-css">   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 }    View Code</code></pre>    <p>怎么样?觉得这些都是小玩意?好吧,都让开,我要开始装逼了!</p>    <p>蒙娜丽莎?什么鬼?我会告诉你这也是一个单标签纯CSS画出来的吗?</p>    <p>几千条box-shadow构成的蒙娜丽莎,看的我内分泌都失调了。。。</p>    <p>如此变态的绘图,都没有怎么用到CSS中最强大的变形,如果加入变形,那可以画出来的形状就更多了。。。更多CSS玩意儿,请到codepen上去探宝吧!</p>    <p>PS:蒙娜丽莎这种图形,可以读取原图信息转换成单位面积的box-shadow,前端用canvas就可以做的,其实这货的技术含量比一个图片图标还要少呢。话虽如此,复杂图形使用CSS来绘制的话,性价比还是太低,建议还是使用图片,这样会更具表现力一些,操作起来也更加简单!专业的绘图还是交给专业的UI去做吧!</p>    <h3><strong>大大小小的坑</strong></h3>    <p>其实,遇到的这些都不能叫做坑,是自己对CSS的理解度不够而已。原以为,将原来icono使用的单位换算成em就算完事了,然而,一改font-size就变形了,顿时懵逼! 究其原因,其实也很简单,并不是所有地方与font-size都是正比的,很多地方混入了线宽的影响,所以要剔除线宽的影响。</p>    <p>去除线宽影响的方法不外乎两种:</p>    <p>1)去掉线宽,例如使用box-shadow等不影响尺寸的属性</p>    <p>2)将线宽纳入计算内,比如translate反向偏移掉线宽,这样整体缩放就不会受到线宽的影响了。</p>    <p>另外一个比较烦的就是居中,其实居中基本上就只用到了下面两种方式,还是蛮简单的。只是,这个反复的copypaste,烦哪!</p>    <p>1. 绝对定位+margin:auto。</p>    <pre>  <code class="language-css">position: absolute;  left: 0;  right: 0;  top: 0;  bottom: 0;  margin: auto;  </code></pre>    <p>实现原理:利用css定位规则,设置左右、上下方向定位为0,margin为auto,让css根据定位计算margin值,用hack的方式实现居中。居中块的尺寸需要可控,因为css计算margin时也需要参考尺寸值,由于四周为0,所以自动计算得到的盒子尺寸(含margin)是与父容器一样的。无论是设置居中块的width、height或者是max-height、max-width,都是让尺寸不会扩大到与父级一样。</p>    <p>局限:在参考系父级(position!=static)大小比本身要小的时候,水平方向的居中就会失效。(垂直方向依然居中)</p>    <p>2. 绝对定位 + transform反向偏移。</p>    <pre>  <code class="language-css">position: absolute;  top: 50%; left: 50%;  transform: translate(-50%, -50%);  margin: auto;   </code></pre>    <p>实现原理:先绝对定位相对父级偏移50%,然后使用transform来反向偏移。由于transform的计算基准是元素本身,所以这里可以用50%来做反向偏移。</p>    <p>局限:这个方案需要固定居中块的尺寸值(不能设置max-width等范围限制),浏览器需要以此为基准来计算定位!</p>    <p>综合来说,方案2的居中方式明显会比方案1要好,但是在绘制图标的时候会用到transform来做一些偏移,为了不覆盖偏移效果所以要用到方案的方式来做居中。除了这两种居中方式,还有inline-block对齐after/before子元素的方式,还有table和flexbox的方式来实现居中,但是画图标本身层级有限而且也用到了before/after,所以不适用图标绘制。</p>    <h3><strong>最后一点</strong></h3>    <p>目前纯CSS的图标还是挺多应用场景的,这种图标的方案免去了做雪碧图和维护雪碧图的麻烦,而且减少了图片资源的请求,从性能上来说会有那么0.01s的提高吧。不用堆雪碧图还方便调整颜色,性能还有0.01s的优化,这套CSS图标你还不赶紧用起来?!</p>    <p> </p>    <p> </p>    <p>来自:http://www.cnblogs.com/qieguo/p/5999630.html</p>    <p> </p>