起因

事件起源于产品迭代会上,产品的一个需求——想让齿轮转起来。第一秒的想法是使用css动画,因为css动画简单易懂开销小,如非必要不太想打扰UI小姐姐专门制作一个gif。

实现

分析

先上图标代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1600412072283"
class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9070"
xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M507.6992 522.88L153.43616 383.03744 507.6992 243.2l363.58144 139.83744z" fill="#B3D7FF" p-id="9071"></path>
<path d="M507.6992 522.88V243.2l363.58144 139.83744z" fill="#CCE4FF" p-id="9072"></path>
<path
d="M745.13408 283.43296h-29.92128a216.8576 216.8576 0 0 0-8.6784-30.73536l25.01632-14.49984a12.53376 12.53376 0 0 0 4.59776-17.15712l-25.12384-43.49952a12.55936 12.55936 0 0 0-17.152-4.59776l-26.04032 15.01184a213.11488 213.11488 0 0 0-22.77376-22.26176l14.49984-25.12384c3.47136-6.02112 1.3312-13.68064-4.69504-17.152l-43.50464-25.12384a12.74368 12.74368 0 0 0-17.152 4.59776l-15.01696 26.04032a196.1984 196.1984 0 0 0-30.94016-7.86432v-28.99968a12.54912 12.54912 0 0 0-12.55936-12.56448h-50.14016a12.57472 12.57472 0 0 0-12.66176 12.56448v30.01856a194.6624 194.6624 0 0 0-30.63808 8.68352l-14.39744-25.12384c-3.47136-6.02112-11.13088-8.064-17.2544-4.59264l-43.50464 25.11872c-6.02624 3.36896-8.06912 11.13088-4.59776 17.15712l15.01184 26.04032a213.11488 213.11488 0 0 0-22.26176 22.77376l-25.01632-14.30016c-6.02624-3.47136-13.68576-1.32608-17.2544 4.59776l-25.12384 43.49952a12.53376 12.53376 0 0 0 4.59264 17.15712l26.04032 15.01184a194.72896 194.72896 0 0 0-7.76192 30.83776h-28.99968a12.54912 12.54912 0 0 0-12.55936 12.56448V351.744a12.54912 12.54912 0 0 0 12.55936 12.56448h30.02368a195.87584 195.87584 0 0 0 8.6784 30.73536l-25.11872 14.49984c-6.02624 3.47136-7.96672 11.13088-4.59776 17.15712l25.12384 43.49952c3.47136 5.92384 11.13088 8.06912 17.152 4.59776l26.04032-15.01184c7.04512 7.96672 14.60224 15.52384 22.77376 22.26176l-14.49984 25.12384a12.53376 12.53376 0 0 0 4.59264 17.152l43.60704 25.12384c5.92384 3.47136 13.68064 1.42848 17.0496-4.59776l15.11424-26.04032a209.67424 209.67424 0 0 0 30.84288 7.76192v29.10208a12.54912 12.54912 0 0 0 12.55936 12.56448h50.34496a12.54912 12.54912 0 0 0 12.55936-12.56448v-30.01856a206.27456 206.27456 0 0 0 30.63296-8.68352l14.50496 25.02144a12.416 12.416 0 0 0 17.0496 4.59264l43.50464-25.11872c6.02624-3.36896 8.06912-11.13088 4.59264-17.152l-15.00672-26.04544a213.11488 213.11488 0 0 0 22.26176-22.77376l25.11872 14.50496a12.53376 12.53376 0 0 0 17.152-4.59776l25.12384-43.49952a12.55936 12.55936 0 0 0-4.59264-17.15712l-26.04032-15.01184a209.95584 209.95584 0 0 0 7.8592-30.83776h29.0048a12.54912 12.54912 0 0 0 12.55936-12.56448v-50.33984a12.54912 12.54912 0 0 0-12.55936-12.56448m-231.7056 140.71808c-55.3472 0-100.28032-44.928-100.28032-100.17792 0-55.4496 44.83072-100.2752 100.28032-100.2752 55.3472 0 100.17792 44.928 100.17792 100.2752 0 55.2448-44.83072 100.17792-100.17792 100.17792"
fill="#61ABFF" p-id="9073"></path>
<path
d="M509.03552 454.17472C439.0912 451.92704 383.1296 394.53696 383.1296 323.97312c0-70.55872 55.9616-127.9488 125.91104-130.2016V79.61088h-23.48544a12.57472 12.57472 0 0 0-12.66176 12.56448v30.01856a194.6624 194.6624 0 0 0-30.63808 8.68352l-14.39744-25.12384a12.65664 12.65664 0 0 0-17.2544-4.59264l-43.50464 25.11872c-6.02624 3.36896-8.06912 11.13088-4.59776 17.152l15.01184 26.04544a213.11488 213.11488 0 0 0-22.26176 22.77376l-25.01632-14.40256c-6.02624-3.47136-13.68576-1.32608-17.152 4.59776l-25.22624 43.49952a12.53376 12.53376 0 0 0 4.59264 17.15712l26.04032 15.01184a194.72896 194.72896 0 0 0-7.76192 30.83776h-28.99968a12.54912 12.54912 0 0 0-12.55936 12.56448V351.744a12.54912 12.54912 0 0 0 12.55936 12.56448h30.02368a207.5648 207.5648 0 0 0 8.6784 30.73536l-25.11872 14.49984a12.53376 12.53376 0 0 0-4.59776 17.15712l25.12384 43.49952c3.47136 5.92384 11.13088 8.06912 17.152 4.59776l26.04032-15.01184c7.04512 7.96672 14.60224 15.52384 22.77376 22.26176l-14.49984 25.12384a12.53376 12.53376 0 0 0 4.59264 17.152l43.60704 25.12384c5.92384 3.47136 13.68064 1.42848 17.0496-4.59776l15.11424-26.04032a206.48448 206.48448 0 0 0 30.84288 7.76192v29.10208a12.54912 12.54912 0 0 0 12.55936 12.56448h17.9712V454.17472z"
fill="#97C5FF" p-id="9074"></path>
<path
d="M513.42848 165.18144c-87.61856 0-158.68928 71.07584-158.68928 158.68928 0 87.61856 71.07072 158.6944 158.68928 158.6944 87.72096 0 158.79168-71.07584 158.79168-158.6944-0.1024-87.61344-71.17312-158.68928-158.79168-158.68928m0 289.09568c-71.99232 0-130.4064-58.30656-130.4064-130.304 0-71.9872 58.41408-130.304 130.4064-130.304 71.99232 0 130.304 58.3168 130.304 130.304 0 71.89504-58.31168 130.304-130.304 130.304"
fill="#86BCFB" p-id="9075"></path>
<path
d="M513.42848 165.18144c-1.4336 0-2.86208 0.1024-4.29056 0.1024v28.3904c1.4336-0.1024 2.86208-0.1024 4.39296-0.1024 71.99232 0 130.304 58.30656 130.304 130.4064 0 71.9872-58.31168 130.304-130.304 130.304-1.536 0-2.96448-0.1024-4.39296-0.1024v28.38528c1.4336 0 2.86208 0.1024 4.29056 0.1024 87.72096 0 158.79168-71.07584 158.79168-158.6944-0.1024-87.71584-71.17312-158.79168-158.79168-158.79168"
fill="#78B3FD" p-id="9076"></path>
<path
d="M509.7472 194.7648v29.40416c1.24416-0.1024 2.38592-0.1024 3.72736-0.1024 61.04576 0 100.01408 49.5616 100.01408 100.00896 0 50.45248-38.8608 100.00896-100.01408 100.00896-1.23904 0-2.4832 0-3.72224-0.1024v29.40928c1.23904 0 2.3808 0.1024 3.72224 0.1024 74.3936 0 129.4848-55.02464 129.4848-129.41824 0-74.3936-55.0912-129.41312-129.4848-129.41312-1.23904 0-2.37568 0-3.72224 0.1024z"
fill="#97C5FF" p-id="9077"></path>
<path d="M153.43616 383.03744v419.52256l354.26304 139.83744v-419.51744z" fill="#66AEFF" p-id="9078"></path>
<path d="M153.43616 383.03744L55.93088 558.7456l354.2528 139.84768 97.51552-175.70816z" fill="#A0C8FA" p-id="9079">
</path>
<path d="M507.6992 522.88l363.58144-139.84256v419.52256L507.6992 942.39744z" fill="#2B8FFF" p-id="9080"></path>
<path d="M507.6992 522.88l363.58144-139.84256 97.52064 175.70304-363.5712 139.84768z" fill="#66AEFF" p-id="9081">
</path>
</svg>

上述是如文章封面中svg图形的源码。可以看到,所有的path并列排布,分别定义了箱子和齿轮的各个由光线和空间划分的子图形。其中,深蓝色齿轮作为本体,内圈左右加了两圈半环,从而体现光影变化。鉴于此,不修改svg本体实现旋转有些麻烦。

分组

svg中有四个主要的元素用于在文档中定义、结构化和引用svg代码。这些元素使得重用svg元素变得容易,同时保持代码的简洁性和可读性。因为svg的特性,这些元素和图形编辑器中的某些命令具有相同的功能。

这四个用于SVG分组和引用的主要元素是:<g>, <defs>, <use><symbol>

于是,我将全部的齿轮path使用<g>标签包裹起来,并加上class="gear-group"以便修改样式。

旋转

为了加入css样式调试,我将svg文件改为html文件,在style标签中编写css样式。于是加上class的svg,操作起来就方便许多了。使用animation属性引用一个旋转动画

1
2
3
4
.gear-group {
animation: roll 2.4s infinite linear;
transform-origin: center 324px;
}

其中animation属性的值分别表示引用的动画名,动画持续时间,动画播放次数(infinite表示无限循环),线性速率播放。

transform-origin定义了变换中心的x和y值。

roll动画定义为

1
2
3
4
5
6
7
8
9
@keyframes roll {
from {
transform: rotate(0deg);
}

to {
transform: rotate(360deg);
}
}

大功告……ehhhh,还没。

这个光影的效果是怎么肥四?看来不可以把整个齿轮当作一个整体来旋转。为了保留光影,只能转齿轮本体。

调整

调整<g>标签的包裹范围,让它只包裹齿轮本体的path。
效果如下图

啊这。考虑把最外层光影也加入旋转,为了保持光影的完整性,最外层的光影不可以旋转一整圈,而是需要最大程度的保持位置,因此将最外层光影的path使用<g>标签包裹,加上class="chip-left-outer"便于控制。同样使用animation属性定义动画

1
2
3
4
.chip-left-outer path {
transform-origin: center 324px;
animation: chipRoll 0.2s infinite linear;
}

为了使齿轮本体和最外层光影的齿轮叠加从而使视觉上的最外层光影完整挡住齿轮本体,chipRoll动画需要使光影在1/12的时间内完成旋转30°,其定义为

1
2
3
4
5
6
7
8
9
@keyframes chipRoll {
from {
transform: rotate(0deg);
}

to {
transform: rotate(30deg);
}
}

结果如下图

这是在意料之中的情况,解决方法不外乎填充遮挡——填充下面动画缺少的部分,遮挡上面动画多余的部分。

填充好说,复制一份最外层阴影,把它和它的动画都旋转-30度,便可以填充下面的缺口。至于遮挡,恰好svg里也有类似css中的clip-path属性以及额外的clipPath元素用来剪切图形。

现在是使用<defs>标签的时候了,svg中的<defs>可以定义一些元素,但是浏览器并不会将它们渲染出来,而是需要其他元素显性调用,类似vue中的component。首先定义clipPath元素,这里简单定义为矩形就可以很好的遮挡齿轮光影。

1
2
3
4
5
6
<defs>
<clipPath id="topClip">
<rect x="0" y="0" width="512" height="600">
</rect>
</clipPath>
</defs>

之后在需要遮挡的<g>标签上增加属性clip-path声明引用即可使光影图形被限制在剪切元素定义的矩形内,超出的部分将被遮挡。

1
<g clip-path="url(#topClip)">

效果如下图

至此算是把“齿轮本体”的旋转解决了。

优化

方案一

以上实现了svg的动画,但是这只是测试,要证明可以在项目里使用,还需要测试项目中的vue组件是否可以用css选中此svg文件。测试结果表明,vue组件是可以选中的。于是这一动画方案可行。

实际上,svg中的<defs>标签还可以加入<style>标签并在其中嵌入css属性,使得旋转动画与svg融为一体,不再需要外部多次定义。

最后再给各css属性加入适配浏览器的前缀-webkit--ms-等。

方案二

关于svg动画,其实还有一个svg本身就有的方法,那就是svg内部的动画元素animate等,详见 SVG 动画元素参考

关于这个方案的实践,在另一篇文章中,点这里

一个loading-更规范的svg动画

结语

当齿轮真正旋转起来后,我们又发现,原先的光影似乎有些问题,让齿轮的旋转看起来不是很自然。但那可能就需要叨扰UI小姐姐了。