如何用Sass和SMIL选项制作SVG加载器的生成器

375 阅读5分钟

在学习Vue.js的同时,我开始建立涉及探索SVG的免费网络工具,目的是为了学习一些关于这两方面的知识让我们来看看这些工具中的一个:一个生成器,它可以制作SVG加载器,让你在SMIL或Sass动画、不同风格、颜色、形状和效果之间进行选择。它甚至可以让你粘贴自定义路径或文本,然后下载最终的SVG,复制代码,或在CodePen上打开一个演示。

它是如何开始的

三个巧合导致我建立了一个SVG加载器的生成器。

巧合一:Sarah Drasner的书

我第一次读到Sass循环是在Sarah Drasner的文章中。 SVG动画.她展示了如何用Sass函数错开动画(就像第6章 "数据可视化的动画 "中的做法)。

我从那一章和Sass循环的可能性中获得了灵感。

巧合2:一个GIF

在同一时刻,我被要求复制一个 "加载器 "元素,类似于苹果的老式经典。

A round segmented spinner where each segment fades in and out in succession to create a circling effect.

这是一个我被要求制作的加载器的模拟图。

我参考了Sarah的例子来实现它。这是我找到的Sass循环代码。

@for $i from 1 to 12 {
  .loader:nth-of-type(#{$i}) {
    animation: 1s $i * 0.08s opacityLoader infinite;
  }
}
@keyframes opacityLoader {
 to { opacity: 0; }
}

这定义了一个从1到12的数字(i)的变量,每一个:nth-child 的元素都会增加动画的延迟。这是一个完美的用例,只需两行Sass代码就可以让我想做多少个元素的动画就做多少个,为我需要的每个延迟节省了CSS声明。这是同一个动画,但用vanilla CSS写的,以显示其不同之处。

.loader:nth-of-type(1) {
  animation: 1s 0.08s opacityLoader infinite;
}
.loader:nth-of-type(2) {
  animation: 1s 0.16s opacityLoader infinite;
}

/* ... */

.loader:nth-of-type(12) {
  animation: 1s 0.96s opacityLoader infinite;
}
@keyframes opacityLoader {
  to { opacity: 0; }
}

巧合3:一个想法

随着这些事情的发生,我有了一个想法,那就是建立一个加载器的画廊,每个加载器都是由相同的Sass循环组成。我总是努力在网上寻找这类东西,所以我想这可能对其他人有用,更不用说我自己。

我以前已经把这种东西作为一个个人项目,所以我最后建立了一个加载器生成器。如果你发现其中的bug,请告诉我!

一个加载器,两个输出

在创建一个能产生正确的Sass输出的生成器时,我被自己的开发技能所阻挡。我决定用SMIL动画尝试另一种动画方法,这就是我最终决定使用的方法。

但后来我得到了一些帮助(谢谢,ekrof!),毕竟让Sass工作了。

所以,我最终在生成器中加入了这两个选项。我发现要让两种语言返回相同的结果是一个挑战。事实上,它们有时会产生不同的结果。

SMIL与CSS/Sass

在这一过程中,我学到了不少关于SMIL和CSS/Sass动画的知识。这些是在制作生成器的过程中帮助我的一些关键收获。

  • **SMIL并不依赖任何外部资源。**它直接通过SVG标记中的表现属性为SVG制作动画。这一点是CSS和Sass都做不到的。
  • **当SVG被嵌入为图像或作为背景图像时,SMIL动画会被保留下来。**直接在SVG中添加一个CSS<style> 块是可能的,但用Sass当然就不行了。这就是为什么在生成器中选择SMIL选项时有一个下载实际SVG文件的选项。
  • **SMIL动画看起来更流畅一些。**我找不到其中的原因(如果有人在这里有更深的信息,请分享!)。我认为这与GPU加速有关,但它们似乎都使用相同的动画引擎。

Two spinners, one left and one right. They are both red and consist of circles that fade in and out in succession as an animated GIF.

SMIL(左)和Sass(右)。

你可能会注意到两种语言之间的动画的不同:

  • 我在SMIL中使用additive="sum" ,一个接一个地添加动画。这样可以确保每一个新的动画效果都能避免覆盖之前的动画。
  • 也就是说,在CSS/Sass中,W3C指出,[当]多个动画试图修改同一个属性时,那么最接近名称列表末尾的动画会获胜。

这就是为什么应用动画的顺序可能会改变Sass的输出。

使用变换的工作

在加载器的造型中使用变换是一个大问题。我将transform: rotate 内联到每个形状上,因为这是一个简单的方法,可以将它们放在一个圆圈中彼此相邻,并且有一个面朝向中心:

<svg>
  <!-- etc. -->
  <use class="loader" xlink:href="#loader" transform="rotate(0 50 50)" />
  <use class="loader" xlink:href="#loader" transform="rotate(30 50 50)" />
  <use class="loader" xlink:href="#loader" transform="rotate(60 50 50)" />
  <!-- etc. -->
</svg>

我可以在SMIL中声明一个type ,与<animateTransform> (例如:scaletranslate )一起,在每个形状的原始变换中加入特定的变换:

<animateTransform
  attributeName="transform"
  type="translate"
  additive="sum"
  dur="1s"
  :begin="`${i * 0.08}s`"
  repeatCount="indefinite"
  from="0 0"
  to="10"
/>

但相反,CSS中的transform 却覆盖了之前应用于内联SVG的任何变换。换句话说,原来的位置被重置为0,显示出与SMIL产生的非常不同的结果。这意味着无论如何,这些动画看起来都是一样的。

The same two red spinners as before but with different results. The SMIL version on the left seems to work as expected but the Sass one on the right doesn't animate in a circle like it should.

使Sass与SMIL相似的解决方案(不是很好)是将每个形状放在一个组(<g> )元素内,并对组应用内联旋转,对形状应用动画。这样一来,内联变换就不会受到动画的影响。

<svg>
  <!-- etc. -->
  <g class="loader" transform="rotate(0 50 50)">
    <use xlink:href="#loader" />
  </g>
  <g class="loader" transform="rotate(30 50 50)">
    <use xlink:href="#loader" />
  </g>
  <!-- etc. -->
</svg>

现在两种语言都有非常相似的结果。

我使用的技术

我使用了Vue.js和Nuxt.js。两者都有很好的文档,但我选择它们有更具体的原因。

我喜欢Vue有很多原因:

  • Vue将HTML、CSS和JavaScript封装成一个 "单文件组件",所有的代码都住在一个文件中,更容易操作。
  • Vue绑定和动态更新HTML或SVG属性的方式是非常直观的。
  • HTML和SVG不需要任何额外的转换(比如让代码与JJSX兼容)。

就Nuxt而言:

  • 它有一个快速的模板,帮助你专注于开发而不是配置。
  • 有自动路由,它支持自动导入组件。
  • 它是一个很好的项目结构,有页面、组件和布局。
  • 由于有元标签,它更容易为搜索引擎优化。

让我们看一下几个加载器的例子

我喜欢的最终结果是,生成器并不是一招鲜。没有一种方法可以使用它。因为它同时输出SMIL和CSS/Sass,所以有几种方法可以将加载器整合到你自己的项目中。

下载SMIL SVG并将其作为CSS中的背景图片使用

就像我之前提到的,当SVG被用作背景图片文件时,SMIL的特性会被保留下来。因此,只需从生成器中下载SVG,将其上传到你的服务器,并在CSS中引用它作为背景图片。

同样地,我们可以用SVG作为一个伪元素的背景图片。

将SVG直接放入HTML标记中

SVG不一定要成为背景图片。毕竟,它只是代码。这意味着我们可以简单地将生成器中的代码放到我们自己的标记中,让SMIL做它的事情。

在内联SVG上使用一个Sass循环

这是我最初想做的,但遇到了一些障碍。与其为每个动画写CSS声明,我们可以使用生成器产生的Sass循环。这个循环的目标是已经应用于输出的SVG的.loader 类。因此,一旦Sass被编译成CSS,我们就会得到一个漂亮的旋转动画。

我还在研究这个

生成器中我最喜欢的部分是自定义形状选项,你可以在其中添加文字、表情符号或任何SVG元素。

The same circle spinner but using custom SVG shapes: one a word, one a poop emoji, and bright pink and orange asterisk.

自定义文本、表情符号和SVG

我想做的是为样式添加第三个选项,使之只有一个元素,你可以用自己的SVG元素工作。这样一来,需要处理的事情就少了,同时允许更简单的输出。

这个项目所面临的挑战是如何处理许多东西的自定义值,比如持续时间、方向、距离和度数。对我个人来说,另一个挑战是对Vue更加熟悉,因为我想回去清理那些混乱的代码。也就是说,这个项目是开源的,我们欢迎拉动请求!请随时发送建议、反馈、和请求。欢迎发送建议、反馈,甚至Vue课程推荐,尤其是与SVG或制作生成器有关的课程。

这一切都始于我在一本书上读到的一个Sass循环。它并不是世界上最干净的代码,但我被SMIL动画的力量所震撼了。我强烈推荐Sarah Soueidan的指南,以便更深入地了解SMIL的能力。

如果你对SMIL的安全性感到好奇,这是有原因的。曾经有一段时间,Chrome将完全废弃SMIL(见MDN中的开篇说明)。但这个废弃计划已经暂停了,而且(似乎)已经有一段时间没有谈及了。

我可以使用SMIL吗?

这个浏览器支持数据来自Caniuse,它有更多细节。一个数字表示该浏览器在该版本及以上支持该功能。

桌面浏览器

浏览器火狐IE边缘浏览器浏览器
54没有796

手机/平板电脑

安卓浏览器安卓火狐安卓iOS Safari
929036.0-6.1