片头警告:涉及动画,流量巨大(看,我还压了个韵)
前两章我可劲的折腾这片云,但都是静静呆在那(静静是谁?),我抬头看天,天上的云可不是,它虽然有时候很缓慢,很缓慢,但好歹也是会动的。作为一个我页面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的长度中循环往复,单一层的动画效果是这样的。
最后,我把叠加三层效果的完整动画云代码贴出来,然后有个问题希望有前端大拿能帮忙解决下:
.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我一直想合成一个函数,减少代码量,但却一直做不到(合起来动画效果会中断,部分动画效果不执行),希望有大拿能够帮忙实现,多谢了。让我们看下最终完全体的动画效果,这次用视频来看。
最后的警告:这个动画效果极其耗电脑CPU和内存,我在写这篇文章的时候,我都能感受到我的笔记本在咆哮(烫手啊),所以实用性要打折扣,大家使用时请自行斟酌。
源代码地址