介绍一种全新的clipPath Sprites小图标技术

这篇文章发布于 2020年10月30日,星期五,01:57,归类于 CSS相关, SVG相关。 阅读 20442 次, 今日 7 次 13 条评论

 

剪裁图标合集技术

目前流行的SVG小图标技术是SVG Sprites技术,此技术我6年前就率先在国内进行了介绍:“未来必热:SVG Sprites技术介绍”,这里再介绍另外一种同样需要用到SVG元素的Sprites技术,我称之为“clipPath Sprites技术”。

一、关于clip-path与小图标实现

CSS clip-path属性除了剪裁圆、多边形之外,还支持url()函数语法,可以把SVG元素中的路径作为剪裁路径。

语法示意如下:

.shape {
    clip-path: url(#someId);
}

someId就是SVG中<clipPath>元素的ID值。

例如:

<svg>
  <clipPath id="someId">...</clipPath>
</svg>

下面通过一个具体案例演示下这种语法的效果。

假设有如下SVG代码:

<svg width="50" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <ellipse cx="25" cy="25" rx="20" ry="10"></ellipse>
    <rect x="15" y="5" width="20" height="40" rx="5" ry="5"></rect>
</svg>

会有如下图所示的姨妈巾效果:

SVG元素图形效果示意

此时,可以把SVG中的图形元素全部放在<clipPath>元素中,这样,就可以把任意的元素剪裁为姨妈巾的形状了。

<svg width="0" height="0">
  <clipPath id="someId">
    <ellipse cx="25" cy="25" rx="20" ry="10"></ellipse>
    <rect x="15" y="5" width="20" height="40" rx="5" ry="5"></rect>
  </clipPath>
</svg>

但是,实际测试发现,上面的代码只有在50px*50px的元素中效果才是正常的,也就是剪裁元素的尺寸默认需要和SVG元素图形尺寸一致才行,这就有些坑爹了,好在SVG提供了一些方法可以适用于任意的HTML元素尺寸。

方法1:transform缩放SVG

例如剪裁的HTML元素的尺寸是20px*20px,原SVG尺寸是50px,因此,需要缩放40%,此时就有如下所示的SVG代码:

<svg width="0" height="0">
  <clipPath id="someId">
    <ellipse transform="scale(0.4, 0.4)" cx="25" cy="25" rx="20" ry="10"></ellipse>
    <rect transform="scale(0.4, 0.4)" x="15" y="5" width="20" height="40" rx="5" ry="5"></rect>
  </clipPath>
</svg>

此时,就可以把20px*20px大小元素剪裁成对应规格的图形效果了:

.icon {
    display: inline-block;
    width: 20px; height: 20px;
    background-color: deepskyblue;
    clip-path: url(#someId);
}

此时就有如下图所示的效果:

transform缩放下的图标效果

方法2:设置clipPathUnits=”objectBoundingBox”

设置剪裁位置是基于对象的尺寸,此时,需要SVG图形中的坐标都以数值1为基准进行计算,因此,通常路径或者参数值基本上都是小于1的数值,例如:

<svg width="0" height="0">
  <clipPath id="someId" clipPathUnits="objectBoundingBox">
    <ellipse cx=".5" cy=".5" rx=".4" ry=".2"></ellipse>
    <rect x=".3" y=".1" width=".4" height=".8" rx=".1" ry=".1"></rect>
  </clipPath>
</svg>  

此时,剪裁的图形效果会按照元素的的尺寸计算,无论图标的尺寸是20px*20px,还是100px*100px,都会按照这个尺寸进行剪裁。

实现的效果如下截图所示:

图标效果示意

眼见为实,您可以狠狠地点击这里:2种方法实现的clip-path小图标剪裁效果demo

无论是transform缩放,还是objectBoundingBox重计算,都可以实现任意尺寸的SVG剪裁效果。

下图是Firefox浏览器下的效果截图:

clip-path实现小图标效果示意

关于IE/Edge浏览器

CSS中有3个很高级属性IE浏览器也是支持的,分别是clip-path属性,filter属性和mask属性。

但是,请注意,这些支持其实并不是CSS规范中的那种支持,而是SVG元素衍生过来的语法,因为IE浏览器支持SVG,而SVG中有一些特性可以在CSS中使用,因此,IE浏览器也支持了部分高级CSS语法,就好像SVG中的fillstroke属性也可以在IE中使用一样。

同样的,clip-path属性,filter属性和mask属性都是仅支持url()语法,均指向SVG元素。

因此,虽然clip-path:url(#someId)在IE/Edge浏览器下语法是合法的,但是仅仅作用在SVG元素上才有效果,也就是下面的HTML代码IE浏览器是没有效果的:

<i class="icon"></i>

但是下面的代码就有剪裁效果:

<svg width="20" height="20">
  <rect fill="deepskyblue" class="icon"></rect>
</svg>

因此,如果需要兼容IE浏览器,只要把普通HTML元素改为使用SVG元素就可以了。

二、更进一步,clipPath Sprites小图标技术

在传统的SVG Sprites中使用的是SVG <use>元素的href属性值指向<symbol>元素的id属性,嘿!岂不是和这里的实现逻辑本质上是一样的,这里是CSS clip-path属性中的url()函数值指向<clipPath>元素的id属性。

因此,理论上,传统SVG Sprites合并那一套东西也是可以用在clipPath Sprites合并中的。

受此idea的启发,我就把我之前制作的SVG压缩合并工具升级了下,增加了clipPath Sprites合并功能(选择的是transform缩放,因为更容易实现[捂脸]),发现这个技术完全可行。

您可以狠狠地点击这里:升级后的SVG在线压缩合并工具

从这个我做的SVG Sprites还原工具中下载几个比较顺眼的图标,然后拖进去——

图标拖拽进去示意

在页面的最下方就可以看到合并后的clipPath Sprites代码了,如下截图示意:

clipPath Sprites代码示意

点击复制或下载按钮,把输入框中的SVG代码复制到页面中,此时,就可以使用CSS clip-path属性实现各式各样的小图标效果了。

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="0" height="0" style="position:absolute;">
    <clipPath id="eye"><path transform="scale(0.034724, 0.0390625)" d="..."/></clipPath>
    <clipPath id="paper-plane"><path transform="scale(0.0390625, 0.0390625)" d="..."/></clipPath>
    <clipPath id="comment"><path transform="scale(0.0390625, 0.0390625)" d="..."/></clipPath>
</svg>  

例如有如下所示的HTML和CSS代码:

<ul id="ul">
    <li><i class="icon" style="--clipPath:url(#paper-plane)"></i>分享</li>
    <li><i class="icon" style="--clipPath:url(#eye)"></i>预览</li>
    <li><i class="icon" style="--clipPath:url(#comment)"></i>评论</li>
</ul>
.icon {
    display: inline-block;
    width: 20px; height: 20px;
    background-color: currentColor;
    -webkit-clip-path: var(--clipPath);
    clip-path: var(--clipPath);
    vertical-align: middle;
    margin-right: 1ch;
}

此时就有如下图所示的小图标效果:

小图标效果实现示意

眼见为实,有演示的,您可以狠狠地点击这里:SVG clipPath合并图标效果demo

由于是通过剪裁实现的,因此实现的图标效果支持任意变色,如下GIF录屏示意(点击播放317K):

颜色变化GIF录屏示意

也可以是渐变效果(改成背景渐变即可),例如:

.icon {
    background: linear-gradient(deepskyblue, deeppink);
    ...
}

渐变效果如下截图:

渐变截图效果

当然,也可以把照片剪裁成图标模板,或者是任意的元素,按钮、文字等都可以剪裁成小图标的样子,非常灵活。

对比传统SVG Sprites技术

传统SVG Sprites clipPath Sprites
矢量
颜色可变
支持渐变
标签 svg>use 任意HTML标签(IE除外)
兼容性 IE9+ IE9+(IE需使用SVG元素)
尺寸控制 灵活 transform方法受限 | clipPathUnits方法灵活
工具 丰富 ✔ 起步中……

整体来看,clipPath Sprites技术的优势更强,尤其在移动端开发中,只要后期有转换工具可以把所有的SVG图标路径转成clipPathUnits为objectBoundingBox模式的数值,那么clipPath Sprites技术几乎就没有任何缺陷,取代传统的SVG Sprites小图标技术应该不成问题。

三、肝不动的结语

本月爆更11篇技术文章,10年来新高,为什么高产四母猪呢?因为《CSS新世界》这本书交稿了,有时候更新博客了,于是积攒的文章一次性爆发了,不过也导致这个月天天很晚睡,实在有些肝不动了。

回到文章这里,clipPath Sprites技术是自己写CSS clip-path章节突然的灵感,我查了下国内外的资料,有使用clip-path实现小图标效果的案例,但是并没有发现谁把小图标全部打包在一起的案例,因此,本文的clipPath Sprites技术可以算是一个首创的技术吧。

不过,也是最近刚折腾出来的,还没有在生成环境使用,因此,是否有什么坑还不得而知,不过按照我长期以来使用clip-path的经验来看,应该没什么问题,clip-path一直是一个运行非常稳健的CSS属性,在移动端兼容性也非常好。

我近期会在合适的项目中尝试这种图标技术,之后有变化会进一步更新。

OK,结语就说这么多。

如果你觉得本文内容确实有点料,欢迎分享,也欢迎以评论方式进行交流,补充我不知道的信息,或者指出文章有表述不准确的地方。

(本篇完)

分享到:


发表评论(目前13 条评论)

  1. 何干说道:

    牛啊,大大减少了http的请求次数。请问用mask 配合mask-position mask-size可以做到类似的事情吗,clip-path绝对路径转相对路径一定会有缺少边角的情况,有啥更好的办法吗?

  2. 龙城男人说道:

    ClipPath Sprites合并后的ID都是随机的数字,这样没法调用啊

    • 张 鑫旭说道:

      估计你没试用过,如果以文件名上传是会有ID的,随机ID是会出现在复制SVG代码粘贴的情况下。

      • 龙城男人说道:

        不好意思,还真是,我之前一直都是找到svg图标复制代码粘贴的,还没试过上传,用了压缩工具好久了,感谢开发这样的工具,方便很多

  3. redbuck说道:

    妙啊.

    这个方案不错.

  4. luokangfei说道:

    https://yoksel.github.io/relative-clip-path/
    大佬 找到这种工具了,正好解决了目前遇到的一些问题,感谢

  5. luokangfei说道:

    请问这样svg需要满足什么样的条件呢,我随便用的svg好像没法用

  6. 果小右说道:

    看了博主的不少关于SVG文章,想深入学习下,想问下有啥SVG书籍推荐嘛~~?

  7. 云中的猫说道:

    实现背景剪裁型的图标可能有应用场景,如果只是普通的小图标,为什么不用字体图标来实现呢?多色图标用clippath似乎也无法实现吧?

    • 码农说道:

      图标颜色用background-color控制,而且可以用渐变颜色,这个用svg实现没那么灵活。

    • 阿龙说道:

      一个是字体,用color控制颜色,一个是剪切,可以剪任何元素。
      总之字体图标尺寸控制更方便,clippath颜色更多变。

  8. MO说道:

    沙发前排?!