首发于编码花
天边一朵云-最终章动画化决定,看云卷云舒

天边一朵云-最终章动画化决定,看云卷云舒

片头警告:涉及动画,流量巨大(看,我还压了个韵)

前两章我可劲的折腾这片云,但都是静静呆在那(静静是谁?),我抬头看天,天上的云可不是,它虽然有时候很缓慢,很缓慢,但好歹也是会动的。作为一个我页面HTML、SVG和CSS的国王、H5统治者、文件的保护者、二进制的MAKER、IE破除者、HEADER领主、DIV主人、帅哥、宅男、造云师,BUG之王怎么能够容忍它一动不动,我跨上我的龙,飞到天上,对着云说:“起来走两步”。

1、会呼吸的云(SVG ANIMATE)

首先,我想到的动画手段是javascript,但我觉得吧,内部矛盾内部解决,既然是svg的事,那么先看看svg自身能不能搞定动画。真别说,还真有,那就是animate。官方的解释是这样的:

animate,动画元素放在形状元素的内部,用来定义一个元素的某个属性如何踩着时点改变。在指定持续时间里,属性从开始值变成结束值。它的属性有这些:

  • attributeName要对哪个属性值做变化
  • attributeType 这个属性值是属于哪个类型的组件(比如CSS,HTML)
  • from 这个属性值要从多少开始变化
  • to 这个属性值变化到哪个值结束
  • dur这个变化过程要持续多久
  • repeatCount 动画过程要持续几次(如果一直反复,用indefinite)

只看这个估计大家印象不深,不如我们开始撸代码吧,先拿一层云来试试手。我通过控制feDisplacementMap中决定云化程度的SourceGraphic值的变化,让云有一种在慢慢飘散的效果。

<div class="cloud" id="cloud-back"></div>
<svg width="0" height="0"> 
    <filter id="filter-back">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" seed="0" />     
      <feDisplacementMap  in="SourceGraphic">
      	<animate attributeName="scale" from="100" to="170" dur="2s" repeatCount="indefinite"></animate>
    	</feDisplacementMap>	
    </filter>
</svg>    

animate作用于feDisplacementMap,所以就放在它的标签里面,云化程度从100到170,变化的过程设置在2秒内完成。最终看到的结果是这样的

云倒是真的动起来了,而且这弥漫的过程,也还真像那么回事,可惜每次循环的时候,都简单粗暴的从头开始,看起来这云跟在跳帧似的。作为造云师,这种云太容易被看出破绽了。万一页面世界的人看到这朵云,发现不是真的,开始觉醒,从 the matrix中吞下红色药丸逃出来,成为the one,和我捣乱怎么办(最近沃卓斯基姐妹(xiongdi)要重启黑客帝国,讲墨菲斯年轻时候的事,这段和本文章无关,也不是广告,就是我的个人爱好)。

循环实在是动画里面的一件麻烦事,通常的思路是把动画的过程分为两段,比如我们这片云,一段是弥漫开,弥漫开后再接一段动画,是云从弥漫到收缩,收缩完了再接上之前弥漫的动画,这样一直循环下去,代码如下:

<feDisplacementMap  in="SourceGraphic">
      	<animate attributeName="scale" id="first" begin="0s;second.end" from="130" to="170" dur="3s" ></animate>
      	<animate attributeName="scale" id="second" begin="first.end" from="170" to="130" dur="3s" ></animate>
</feDisplacementMap>

这里面有几点要注意:

  • 加了id,方便标记两段动画的设置
  • 加了begin属性,用来衔接第一段动画的开始和第二段动画的开始,第二段结束后回到第一段的开始
  • 去掉了repeatCount属性,如果这个属性还在,只会循环第一段动画配置,不会跳到第二段,哪怕你配置了begin也不行

加后,看到的效果是这样的

变化不是很明显,请看官盯仔细了

这样,云的简单动画就完成了,然后为了效果,我们把三层云叠加上,体现出明暗的立体效果。

#cloud-back {
  filter: url(#filter-back);
  box-shadow: 300px 300px 30px -20px #fff;
}
#cloud-mid {
  filter: url(#filter-mid);
  box-shadow: 300px 340px 70px -60px rgba(158, 168, 179, 0.5);
    left: -25vw;
}
#cloud-front {
	 filter: url(#filter-front);
	box-shadow: 300px 370px 60px -100px rgba(0, 0, 0, 0.3);
    left: -25vw;
}


<div class="cloud" id="cloud-back"></div>
	<div class="cloud" id="cloud-mid"></div>
	<div class="cloud" id="cloud-front"></div>
<svg width="0" height="0"> 
    <filter id="filter-back">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" seed="0" />     
      <feDisplacementMap  in="SourceGraphic">
      	<animate attributeName="scale" id="first" begin="0s;second.end" from="130" to="170" dur="2s" ></animate>
      	<animate attributeName="scale" id="second" begin="first.end" from="170" to="130" dur="2s" ></animate>
    	</feDisplacementMap>	
    </filter>
    <filter id="filter-mid">
        <feTurbulence type="fractalNoise"  baseFrequency="0.012" numOctaves="2" seed="0"/>
      <feDisplacementMap  in="SourceGraphic">
      	<animate attributeName="scale" id="first1" begin="0s;second1.end" from="100" to="140" dur="2s" ></animate>
      	<animate attributeName="scale" id="second1" begin="first1.end" from="140" to="100" dur="2s" ></animate>
    </feDisplacementMap>	
    </filter>
    <filter id="filter-front">
        <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" seed="0"/>
      <feDisplacementMap  in="SourceGraphic">
      	<animate attributeName="scale" id="first2" begin="0s;second2.end" from="80" to="110" dur="2s" ></animate>
      	<animate attributeName="scale" id="second2" begin="first2.end" from="110" to="80" dur="2s" ></animate>
      </feDisplacementMap>		
    </filter>
</svg>    

最后呈现出的效果是这样的

同样需要仔细看才能看出细节的动画

2、看云卷云舒(JQUERY ANIMATE)

我是个不容易满足的人,上面的云虽然动起来了,但太小家子气,不仔细看,真看不出来是动的,为了让它能以肉眼可见的变化动起来,我决定在上面动画的基础上再加戏,动的猛一点,这次,用上大家熟悉的老朋友,jquery.animate(),让云舒展开来。

var cssback = {width:'700px'};
		$("#cloud-back").animate(cssback,12000,goBack);
		function goBack(){
			if(cssback.width==='700px')
				cssback.width='300px';
			else if(cssback.width==='300px')
				cssback.width='700px';
			$("#cloud-back").animate(cssback,12000,goBack);
		}

cssback是我们需要变化的属性值,12000是动画的时间长度,goBack是动画执行完后执行的函数。goBack其实和上面svg的animate一样,是解决循环问题的,通过这个递归函数,可以让用户在300-700px的长度中循环往复,单一层的动画效果是这样的。

这个效果叠加了svg animate和jquery animate两种动画效果

最后,我把叠加三层效果的完整动画云代码贴出来,然后有个问题希望有前端大拿能帮忙解决下:

.cloud {
  width: 300px;
  height: 275px;
  border-radius: 50%;
  position: absolute;
  top: -35vh;
  left: -25vw;
}

#cloud-back {
  filter: url(#filter-back);
  box-shadow: 300px 300px 30px -20px #fff;
}

#cloud-mid {
  filter: url(#filter-mid);
  box-shadow: 300px 340px 70px -60px rgba(158, 168, 179, 0.5);
    left: -25vw;
}

#cloud-front {
	 filter: url(#filter-front);
	box-shadow: 300px 370px 60px -100px rgba(0, 0, 0, 0.3);
    left: -25vw;
}


<div class="cloud" id="cloud-back"></div>
	<div class="cloud" id="cloud-mid"></div>
	<div class="cloud" id="cloud-front"></div>

<svg width="0" height="0"> 
    <filter id="filter-back">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" seed="0" />     
      <feDisplacementMap  in="SourceGraphic">
      	<animate attributeName="scale" id="first" begin="0s;second.end" from="130" to="170" dur="3s" ></animate>
      	<animate attributeName="scale" id="second" begin="first.end" from="170" to="130" dur="3s" ></animate>
    	</feDisplacementMap>	
    </filter>
    <filter id="filter-mid">
        <feTurbulence type="fractalNoise"  baseFrequency="0.012" numOctaves="2" seed="0"/>
      <feDisplacementMap  in="SourceGraphic">
      	<animate attributeName="scale" id="first1" begin="0s;second1.end" from="100" to="140" dur="3s" ></animate>
      	<animate attributeName="scale" id="second1" begin="first1.end" from="140" to="100" dur="3s" ></animate>
    </feDisplacementMap>	
    </filter>
    <filter id="filter-front">
        <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" seed="0"/>
      <feDisplacementMap  in="SourceGraphic">
      	<animate attributeName="scale" id="first2" begin="0s;second2.end" from="80" to="110" dur="3s" ></animate>
      	<animate attributeName="scale" id="second2" begin="first2.end" from="110" to="80" dur="3s" ></animate>
      </feDisplacementMap>		
    </filter>
</svg>


var cssback = {width:'700px'};
		$("#cloud-back").animate(cssback,12000,goback);
		function goback(){
			if(cssback.width==='700px')
				cssback.width='300px';
			else if(cssback.width==='300px')
				cssback.width='700px';
			$("#cloud-back").animate(cssback,12000,goback);
		}
		var cssmid = {width:'700px'};
		$("#cloud-mid").animate(cssmid,12000,goBackmid);
		function goBackmid(){
			if(cssmid.width==='700px')
				cssmid.width='300px';
			else if(cssmid.width==='300px')
				cssmid.width='700px';
			$("#cloud-mid").animate(cssmid,12000,goBackmid);
		}
		var cssfront = {width:'700px'};
		$("#cloud-front").animate(cssfront,12000,goBackfront);
		function goBackfront(){
			if(cssfront.width==='700px')
				cssfront.width='300px';
			else if(cssfront.width==='300px')
				cssfront.width='700px';
			$("#cloud-front").animate(cssfront,12000,goBackfront);
		}

最后这段js的代码,goBack,goBackmid和goBackfront我一直想合成一个函数,减少代码量,但却一直做不到(合起来动画效果会中断,部分动画效果不执行),希望有大拿能够帮忙实现,多谢了。让我们看下最终完全体的动画效果,这次用视频来看。

完全体云卷云舒https://www.zhihu.com/video/1126538684589383680

最后的警告:这个动画效果极其耗电脑CPU和内存,我在写这篇文章的时候,我都能感受到我的笔记本在咆哮(烫手啊),所以实用性要打折扣,大家使用时请自行斟酌。

源代码地址

发布于 2019-06-24

文章被以下专栏收录