实现SVG图标的渐变填充效果

2018-05-16 阅读 872 收藏 1 原链:www.zcfy.cc
分享到:

前端必备图书《CSS重构 样式表性能调优 css代码重构指南 软件架构优化测试 css参考工具书》 >> >> 

2016年我写过一篇文章如何使用SVG图标,其“试验性部分”的结语是个警告 - “抱歉,渐变填充无法工作”。

我指的是像fill: linear-gradient(red, blue)一样的无效代码,因为fill属性出自SVG - 该元素内置了独有的渐变支持;而linear-gradient则源自CSS,常用于设置背景。二者的结合效果并不理想。

我可以使用SVG的<linearGradient/>元素,但之前并未尝试过。几个月后我做了试验,效果还算不错。下面请看分享。

在HTML中添加渐变

我发现最值得信赖的方式是在HTML页面中添加SVG元素(例如在<body>标签的开始或结束位置添加)。这里应该定义一个<linearGradient>

<svg style="width:0;height:0;position:absolute;" aria-hidden="true" focusable="false">
  <linearGradient id="my-cool-gradient" x2="1" y2="1">`
    <stop offset="0%" stop-color="#447799" />
    <stop offset="50%" stop-color="#224488" />
    <stop offset="100%" stop-color="#112266" />
  </linearGradient>
</svg>

这个元素不推荐用display:none实现隐藏效果,这样的话某些浏览器会忽略渐变效果。把元素高度设为0px来实现隐藏效果是可行的。

现在我们可以在SVG图标上使用渐变效果了:

<svg class="icon" fill="url(#my-cool-gradient) #447799;" aria-hidden="true" focusable="false">
  <use xlink:href="#symbol-id"></use>
</svg>

或者在CSS里这样写:

.icon {
  /* gradient and fallback color */
  fill: url(#my-cool-gradient) #447799;
}

下面这两个图标应该使用了松石绿-深蓝的渐变。示例:Leaf by Gabriele Malaspina , Carpet by Ben Davis

不过我们无法定制单个按钮的渐变。如果想这样做,需要在HTML中创建不同的SVG渐变定义。

使用CSS变量控制渐变色

如果我们想用CSS设置渐变色,可以通过CSS变量来实现。我们将使用CSS自定义属性来编写渐变定义(var(--my-custom-property)).

<svg aria-hidden="true" focusable="false" style="width:0;height:0;position:absolute;">
  <linearGradient id="gradient-horizontal">
    <stop offset="0%" stop-color="var(--color-stop-1)" />
    <stop offset="50%" stop-color="var(--color-stop-2)" />
    <stop offset="100%" stop-color="var(--color-stop-3)" />
  </linearGradient>
  <linearGradient id="gradient-vertical" x2="0" y2="1">
    <stop offset="0%" stop-color="var(--color-stop-1)" />
    <stop offset="50%" stop-color="var(--color-stop-2)" />
    <stop offset="100%" stop-color="var(--color-stop-3)" />
  </linearGradient>
</svg>

现在我们可以设置 - 如果需要的话 - 在CSS中还能改变这些颜色:

#gradient-horizontal {
  --color-stop-1: #a770ef;
  --color-stop-2: #cf8bf3;
  --color-stop-3: #fdb99b;
}
#gradient-vertical {
  --color-stop-1: #00c3ff;
  --color-stop-2: #77e190;
  --color-stop-3: #ffff1c;
}

最后,用这些效果来填充图标:

.icon-hgradient {
  fill: url(#gradient-horizontal) gray;
  /* We could use it as a stroke fill too:
  stroke: url(#gradient-horizontal) gray; */
}
.icon-vgradient {
  fill: url(#gradient-vertical) gray;
}

下面是浏览器的支持度(浏览器对CSS自定义属性的支持程度):

使用SVG渐变填充和CSS变量测试图标:

渐变填充会绘制图标的每条路径。为了避免颜色衔接不当(譬如叶子和茎的连接处),把图标的源SVG所有路径或大部分路径合并可能有效果。

在外部文件中使用渐变

这个技巧在FireFox中被证实有效,也曾适用于Edge(Edge14,15确认可用,但Edge16和预览版又取消了支持)。请看下面的测试:

  1. 两个图标都取自一个外部雪碧图: sprite.svg.
  2. 第一个图标使用了HTML页面的渐变效果,第二个则使用了sprite.svg.
.icon-sprite-gradient {
  fill: url(sprite.svg#my-warm-gradient) red;
}

不支持此项操作的浏览器(Chrome,Safari,最新版Edge…)应该为第二个图标展示一个红色的替代填充效果。

在CSS中使用数据URL作为渐变值

如果我告诉你可以在SVG中定义渐变之后把SVG作为数据URL放在CSS中,你会怎么想? 好吧我承认是在犯傻,可它真的可以实现! 至少在Firefox中可以:P

/* Notice the  id="grad" in the SVG URL and how we’re using it at the end. Note that the # in hexadecimal colors must be escaped as %23\. */
.icon-gradient-url {
  fill: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><linearGradient id='grad'><stop offset='0%' stop-color='%23ff00cc'/><stop offset='100%' stop-color='%23333399'/></linearGradient></svg>#grad") purple;
}

/* Same SVG, in base64 */
.icon-gradient-base64 {
  fill: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZCc+PHN0b3Agb2Zmc2V0PScwJScgc3RvcC1jb2xvcj0nI2ZmMDBjYycvPjxzdG9wIG9mZnNldD0nMTAwJScgc3RvcC1jb2xvcj0nIzMzMzM5OScvPjwvbGluZWFyR3JhZGllbnQ+PC9zdmc+#grad") purple;
}

看到我们如何在URL的结尾用#grad引用渐变效果的id了吗?目前看来只有Firefox能理解这个语法。额,太遗憾了不是吗。

这个例子打算实现一个品红色-紫色渐变效果。不支持此操作的浏览器(Chrome,Safari,Edge…)应该会展示一个紫色的备用填充效果。

概括

  1. 是的,我们可以使用渐变填充!
  2. 但首先需要在HTML中添加SVG渐变
  3. 可以在SVG渐变中直接设置颜色(当然是HTML中的SVG),或者在CSS里设置(使用CSS变量)
  4. 所有使用给定渐变效果的图标会使用相同的颜色,但我们无法像下面一样覆盖某种特定颜色:
.icon-gradient-override {
  /* 可行方法 */
  fill: url(#gradient-horizontal) gray;
  /* 以下方法法不可行... */
  --color-stop-1: white;
  --color-stop-2: gray;
  --color-stop-3: black;
}

假如你需要大量渐变效果,这个技巧可能不适合你。因为在HTML中创建10个或20个不同的SVG渐变并不实用。但对于简单需求而言,这个技巧在所有现代浏览器中均可使用(如果我们放弃CSS变量的话,IE11也在支持的浏览器之列)。

SVG
回到顶部