我在 SVG 中使用和动画化圆锥渐变时的挣扎

Avatar of Amit Sheen
Amit Sheen

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

我为之工作的这家很棒的公司,Payoneer,有一个新的徽标,而我的工作是为我们应用程序中的加载器组件重新创建并动画化它。我将详细解释我是如何做到的,分享我遇到的问题,并带您逐步了解我提出的解决方案。另外,作为奖励,我们将看看如何对其进行动画处理!

但首先,我想有些人会问自己...重新创建它?为什么?

设计我们徽标的品牌机构向我们发送了一套完整的资产,按主题分类。它们有各种尺寸,并以所有可用的格式提供。我们拥有所有东西,包括徽标和加载器动画的 SVG。但我们无法使用它们。

原因如下。让我们看一下徽标

我们称新徽标为“光环”。

徽标是一个带圆锥渐变的环,由五种颜色组成,仅此而已。问题是 SVG 不支持角度渐变(至少目前还没有),因此当我们将具有圆锥渐变的设计导出为 SVG 时,我们需要某种技巧才能获得所需的结果。

现在,我并不是矢量图形软件方面的专家,所以可能存在另一种(也许更好)的方法来做到这一点,但我知道将圆锥渐变导出到 SVG 的最常见方法是将渐变元素转换为图像,并将该图像作为 base64 字符串插入 SVG 中。这也是我们从品牌机构获得的,我相信他们知道导出 SVG 的最佳方式。

但是,由于最终的 SVG 文件现在包含一个 PNG base64 字符串,因此文件大小跳到了近 1MB,这可能不算什么灾难,但远高于它应该的 2KB。将这种差异乘以三个主题(无文字、浅色文字和深色文字变体),我们最终得到的是 3MB 的图像,而不是 3KB 的代码。这之间有很大差异,因此我们决定使用 SVG 重新创建徽标。

但是怎么做呢?

尽管CSS 完全支持圆锥渐变,但 SVG 不支持。因此我问自己的第一个问题是如何在 SVG 中创建圆锥渐变。实际上,我问了谷歌。我发现了很多创建 SVG 圆锥渐变的酷炫、独特、有创意的方法,其中大多数都依赖于某种clip-path实现。我首先创建了一个简短的<path>来表示环的形状,并将其用作简单<rect>元素的clip-path

接下来,我需要用圆锥渐变填充<rect>,但首先,我必须找到所有正确的颜色停止点才能重现外观。这花费了一些时间,但在经过大量的微调后,我得到了一个我很满意的结果

div.gradient {
  background-image: conic-gradient(from 270deg, #ff4800 10%, #dfd902 35%, #20dc68, #0092f4, #da54d8 72% 75%, #ff4800 95%);
}

最后一步是用其他支持圆锥渐变的东西替换<rect>,我发现最简单的方法是使用一个带有普通<div>的 SVG <foreignObject>元素,以及作为background-imageconic-gradient。然后我需要做的就是设置<foreignObject>元素的clip-path,就完成了。

所以,这就是我在 SVG 中使用圆锥渐变的方式,以保持设计完全矢量化且可扩展,代码少于 20 行,文件大小少于 2KB。

但这只是简单部分。现在让我们谈谈动画。

加载器

我们的应用程序在用户每次登录时都会显示一个加载动画。我们一直在使用 GIF 文件,但几个月来我一直想将其更新为纯 CSS/SVG 动画。好处显而易见:更快的渲染意味着更流畅的加载体验,更小的文件大小意味着更快的加载速度。我们只需要更少的东西就能获得更多,这对于加载动画来说尤其理想。

这是我想要实现的动画

这种类型的动画实际上使用 SVG 非常容易。我们只需要一个技巧,使用stroke-dasharraystroke-dashoffset。这是我的起点。我在环的中心创建了一个新的<path>,删除了fill,添加了具有正确stroke-widthstroke,然后处理动画。

我玩了一会儿才让它按照设计师想要的方式移动。实际上,我最终使用了两个动画:一个控制stroke-dashoffset,另一个将整个<path>旋转一圈。

但是,由于clip-path属性指的是形状的fill,因此对描边进行动画处理意味着我必须解决两个问题之一:我可以找到另一种方法来对运动进行动画处理,或者找到另一种方法来将颜色添加到描边。

因此,我回到了谷歌,以及我之前找到的所有创意,但大多数都无法进行动画处理,所以我开始寻找一个好的非clip-path方法来将颜色添加到描边。我查看了一些“开箱即用”的解决方案,查看了遮罩,并最终找到了最简单、完美的解决方案

.logoBlend {
  mix-blend-mode: lighten;
}

lighten混合模式会查看渲染元素的每个像素的 RGB 颜色,将其与后面的背景像素的 RGB 值进行比较,并保留较高的那个。这意味着元素中白色的部分将保持白色,而深色的部分将获得背景像素的值。

通过向黑色路径添加一个白色的<rect>,我实际上阻止了任何位于它后面的东西。同时,位于动画黑色描边后面的所有内容都是可见的。这意味着我可以重新引入带有conic-gradient<foreignObject>,将其放置在mix-blend-mode层后面,并为其提供一个简单的旋转动画来匹配设计。

请注意,此方法的最终结果将具有白色背景,而不是像静态徽标一样透明,但我对此感到满意。如果您需要,可以反过来,使用黑色背景,并将元素的浅色部分隐藏,方法是将混合模式设置为darken

最后的润色

到目前为止,我已经基本完成了,并且对最终结果感到非常满意。但几天后,我从品牌机构那里收到一个基于Lottie的 JSON 文件,其中包含完全相同的动画。回想起来,也许我可以省去工作并使用他们的文件,它应该可以正常工作。甚至文件大小也出奇地小,但它仍然比 SVG 大 8 倍,所以我们最终还是使用了我的动画。

但是,这意味着我还有最后一件事情要做。Lottie 动画有一个“启动动画”,其中一个小橙点逐渐变大并进入视野,我必须将其添加到我的动画中。我在所有三个动画中添加了一个 0.5 秒的延迟,以及一个开始时的缩放动画。

点击笔上的“重新运行”以再次查看从初始点开始的动画。

就是这样!现在我的公司有了新的徽标,以及一套轻量级、完全可扩展的资产,可以在我们的 Web 平台上使用。

对于那些想知道的人来说,是的,我最终在 React 中创建了一个不错的徽标组件,因为我们正在使用它。它甚至根据作为道具传递给它的主题渲染 SVG,使实现变得更加容易,并将所有未来的更改保留在一个位置。

你呢?

您认为有更好的方法可以获得相同的结果吗?在评论中分享您的想法!感谢您的阅读。