CSS3动画实战之多关键帧实现无限循环动效的时间间隔

题目有点绕,源起最近一个项目中所需的一枚loading图标。SVG+CSS3动画作了那么多,真正应用在项目中的机会少之又少,因此,抓住一切机会,即便loading也不能放过,用系统自带菊花有辱我这一年的修炼。在最后完美作成的过程当中,解决了两个问题,第一,是非等粗交叉路径的描边动画实现,第二,是多个拼接动画的无限循环问题,后者困扰了许久,因此,当这个问题解决时,急于分享出来,便于其余的设计师小伙伴遇坑时一笑而过,也就是这篇文章的原由了。优化

1.若是只是简单的描边动画

是的,我是说若是只是若是。先看下要实现的动效。动画

左边是真身,很简单的一个企业的logo,由于是这种连笔式的,因此本能的反应适合描边动画,动态展现logo绘制过程。右边就是初步想法,绘制过程。看上去很简单,啊哈,来,透过现象看本质。若是这个图标是下面这种,对,就是我曾经的心头好,网易云音乐,由于随手用钢笔画的,没有用布尔运算,因此略显粗糙。这种来个描边动画,那简直是分分钟搞定的事情。

这种描边动效经过定义 stroke-dashoffset属性来实现(from 0; to length),很是简单,之前的文章中也写过 戳这里戳这里,此处略过。好了,为何说这种好实现呢,由于描边只要定义三个样式的属性值 stroke-linecapstroke-linejoinstroke-width就好。

好了,不说别人的logo描边动效多好实现了,来分析一下如今的案例,难度在哪里?非线性啊亲们。若是这个图标是下面这种的:url

简单不简单,你就说简单不简单!固然,让甲方爸爸改图标是不现实,若是妥协作个神似形不似版的那还不如菊花了(转行作美工久了,没点强迫症还真是不行)。如今开始,找解决方法,突破,分析问题的能力仍是有的。我想到的方法是万能的蒙版。蒙版是个好东西啊,能遮住全部你不想看到的,那直接给路径描边动画加个蒙版就行了呗。spa

白色蒙版是logo部分,其他黑色的部分遮住。之因此描边给了很粗,就是由于这个logo自己起点处较粗,须要加粗的描边路径通过全部的logo部分。那么,还觉得这样就完了?

2.若是只是路径不重合的描边动画

是的,我是说若是只是若是。继续举个栗子,若是是下面这种logo,这事就又简单多了。这是个什么,鬼知道,我就随手搞了一个不等粗的描边而已。设计

看,上面分析的使用蒙版的思路也是对的吧?轻松实现了不等粗的logo描边效果。

那来看看真实案例,准备好,打脸( ̄ε(# ̄)☆╰╮( ̄▽ ̄///)。效果是这样的!!!3d

其实 也蛮好理解的,主要是交叉部分出了问题。当第一遍描边动画路过交叉点的时候,已经透过蒙版显示了与描边等宽的部分。code

3.来吧,解决问题吧

固然,这点区区的小困难是不会让我放弃的。既然在交叉点那里纠缠不清,那就快刀斩乱麻,把路径剁开就好(暴露了暴力的本性)。cdn

每段各司其职,定义好时间延迟,ok了。简单贴上点代码凑凑字数,CSS部分blog

/* 定义一个统一的改变stroke-dashoffset值的动画属性*/
	@keyframes  dash{
	to {stroke-dashoffset: 0;}
    }
	@keyframes  dash{
	#MH_Path1{
	stroke-dasharray:705;  /* 705为第一段分段路径的长度*/
	stroke-dashoffset:705;     
	animation: dash 0.7s linear  forwards; /* 0s开始,持续0.7s*/
	}
	#MH_Path2{
	stroke-dasharray:645;  /* 645为第二段分段路径的长度*/
	stroke-dashoffset:645;
	animation: dash 0.6s linear  0.7s forwards;  /* 延迟0.7s开始,持续0.6s*/
	}
	#MH_Path3{
	stroke-dasharray:108;  /* 108为第三段分段路径的长度*/
	stroke-dashoffset:108;
	animation: dash 0.1s linear   1.3s forwards;   /* 延迟1.3s开始,持续0.1s*/
	}
复制代码

DOM部分,由于三部分蒙版图形要被复用一次做为浅灰色logo底图(或者也能够单独导出路径,但毕竟不是最优化的方法),因此我用<symbol>来定义三部分的图形。get

<symbol id="logo1">
	<path d="" /> <!-d值对应第一部分蒙版的路径-->
	</symbol>
	<symbol id="logo2">
	<path d="" /> <!-d值对应第二部分蒙版的路径-->
	</symbol>
	<symbol id="logo3">
	<path d="" /> <!-d值对应第三部分蒙版的路径-->
	</symbol>
	
	<!--定义三部分蒙版,用use标签去引用 -->
	<mask id="MH1"><use xlink:href="#logo1" /></mask>
	<mask id="MH2"><use xlink:href="#logo2" /></mask>
	<mask id="MH3"><use xlink:href="#logo3" /></mask>

	<!--底层浅灰色logo-->
	<g fill="#ede8e6">
	<use xlink:href="#logo1" />
	<use xlink:href="#logo2" />
	<use xlink:href="#logo3" />
	</g>
	
   <path  mask="url(#MH1)"  id="MH_Path1"    d="" /> <!-d值对应第一段分段路径-->
	<path  mask="url(#MH2)"  id="MH_Path2"    d="" /><!-d值对应第二段分段路径-->
	<path  mask="url(#MH3)"  id="MH_Path3"    d="" /><!-d值对应第三段分段路径-->
复制代码

通过路径和蒙版的剪切,获得了下面这枚半成品的loading logo。

4.或许,这里才是真正的干货

看起来彷佛没有问题了,蒙版拼接+路径拼接,交叉点的问题已然解决。但这不过是SVG+CSS3动效的活学活用,这样的案例随随便便拿一个来均可以分析,不足以成文。

loading图标算是完成了,but just done ,not perfect。咱们都知道,加载的时间是不可控的,那么,完成一次描边动画后,理论上应该开始下一轮,animation有个属性是animation-iteration-count,也就是动画播放次数,像咱们转圈圈的菊花图标,通常都会定义值为infinite,也就是无限循环,那在这个案例中,问题出在什么地方呢?

再回过头看咱们的描边动画属性的定义,我以最有表明性的第二段为例:animation: dash 0.6s linear 0.7s forwards,后面的0.7s是动画延迟开始的时间,在进行无缝拼接的时候,第二段描边动画开始的延迟时间就是第一段动画的时间,同理,第三段动画开始的延迟时间为动画一加动画二,当没有定义执行次数时,默认执行一次。那么,当咱们加上这个无限循环的属性值以后,来看看动画变成了什么样子。

看上去乱七八糟,那是由于被切割的每段都在单独执行本身的循环,是的,动画执行的次数能够无限循环,但延迟只能被执行一次,并无什么特殊的属性能够控制在每一个循环开始以前都执行延迟。至少如今没有,但CSS3是否是能够考虑加上新的规范(又在浮想翩翩中,醒醒吧)。经过最经常使用的infinite属性来控制无限循环的泡沫已然破碎,但这个问题会是无解的么?(又在废话,无解的话这篇文章意义何在?)

如今来想一下,控制延迟时间,除了直接在animation属性中直接写时间值来定义,还有什么方法。对,就是关键帧@keyframes(嗯,俨然又开启了愉快的自问自答模式)。从如今开始,为了infinite属性能够发挥做用,个人三部分动画再也不设任何延迟,共用相同的全程动画时间,取而代之经过@keyframes来控制开始和结束的时间。看一下下面这张图或许有助于理解:

下面,我要经过控制@keyframes的时间节点来控制每段动画的时间区间,至于为何选择45%和90%做为节点,无他,只是估摸了一下每段动画的时间的比例,为了好计算而已。我把整个动画时间周期设计成了2s,DOM部分没有变化,但CSS部分须要彻底从新定义。首先,通用的改变stroke-dashoffset值的动画属性的设置已经无用了,由于不是从0%到100%执行,而是打断了,具体的打断方法各个部分又有所不一样。

@keyframes  MH_Path1{
	0%{stroke-dashoffset:705;}
	45%, 100%{stroke-dashoffset: 0;}
	}
	/* 0s开始,持续0.9s,1.1s延迟*/

	#MH_Path1{
	stroke-dasharray:705;
	animation: MH_Path1 2s linear both infinite;
	}

	@keyframes  MH_Path2{
	0%, 45% {stroke-dashoffset: 645;}
	90%, 100%{stroke-dashoffset: 0;}
	}
	/* 0.9s开始,持续0.9s,0.2s延迟*/

	#MH_Path2{
	stroke-dasharray:645;
	animation: MH_Path2 2s linear both infinite;
	}

	@keyframes  MH_Path3{
	0%, 90% {stroke-dashoffset: 108;}
	100%{stroke-dashoffset: 0;}
	}
	/* 1.8s开始,持续0.2s*/

	#MH_Path3{
	stroke-dasharray:108;
	animation: MH_Path3 2s linear both infinite;
	}
复制代码

在第一段路径中,我在45%处即执行完成了整个描边动画过程,而剩下的从45%到100%部分,由于没有任何变化,因此天然而然的生成了延迟时间,对应定义45%, 100%{stroke-dashoffset:0;};第二段则须要同时控制开始和结束,同理,第三段则只须要控制开始时间。

是时候检验一下效果了:

若是是整个动画须要延迟开始,那就简单的多,只须要在每一个animation属性中写入须要延迟的时间就能够了,三个不分彼此,相同的定义,完美共享。

在作此次案例的过程当中,最大的收获就是经过定义关键帧实现了多个拼接的动画(或者称之为有延迟效果的动画)的无限循环问题,因此,你压轴!

codepen预览在这里,戳戳戳

相关文章
相关标签/搜索