如何让你的动画更自然

本文将从为什么要探究更自然的动画、如何探究运动曲线方程、列举常用的运动曲线、分别使用js和css实现曲线动画效果、可视化实现工具这几个方面进行介绍。希望阅读后,本文能给你在制作动画效果时带来一点帮助。

1.为什么需要探究更自然的动画

自css animation推出后,强大的功能使得我们通过css也能制作出媲美flash的动画效果。然而在制作动画的时候,我们也许会常常纠结怎么设置timing-function。一般情况下,我们会直接使用自带的五个动画函数(linear、ease、ease-in、ease-out、ease-in-out),或是在cubic-bezier.com创建一些自定义的动画函数(cubic-bezier(n,n,n,n))。但往往这一切都只是局限于使用,而不知道其原理究竟是什么,以及没有背后的物理原理支撑,使得做出来的动画可能会变得有点形而上学。例如用ease-in来做小球从高处掉下的效果,这个加速效果没有遵循相关物理原理,使得出来的动画效果不太自然。

自然的动画效果应该是和我们在现实生活中看到的物体运动轨迹相似的。这样的效果往往与背后的运动曲线函数紧密联系在一起。如上面提到的小球从高处掉下效果,对应的是匀加速运动函数s1=0.5gt²。若再探讨之后受到空气阻力及接触面材质影响,回弹的高度s2=s1*n(0<n<1,可以假定n=0.64),如此循环下去,直至小球最后停在地上,这样就可以模拟出整个小球掉下效果

现实生活中的运动效果丰富多样,只靠css3提供的几个基本动画函数是不足以模拟的,例如弹簧动画效果等。要模拟这些真实的效果,就要学会如何获得这些效果背后的动画函数了。

下图是用了弹簧曲线效果和只用基本的动画曲线效果的弹窗对比:


2.探究运动曲线方程

以下以弹簧动画为例,探究一下怎样模拟出这个效果。

ios9提供了CASpringAnimation类实现该效果,而web上就没有提供类似函数。但我们仍然可以通过以前学过的物理学和数学知识来做一下研究。

下面有一个弹簧块,假设它质量为1,在它不动的时候位置是x = 1,则拉伸时的距离就是x-1了:

弹簧示例

将这比作一个动画,弹簧块在时间t时所处的位置x就可以看作动画曲线函数x = f(t)。如果我们求得这个函数公式,就可以模拟出这个动画效果了。对此,下图将通过物理学公式和数学知识进行探讨。

Wolfram | Alpha中输入以上公式后得出

使用工具绘制函数得:

感觉还是蛮像一个弹簧曲线的运动轨迹的嘛。像这样,如果我们要模仿自然生活中的某个运动轨迹,可以如上探究一下背后的物理方程,运用数学知识计算,和使用合适的工具,来模拟出对应的运动曲线。但估计很多人都把这些知识还给老师了,因此如果所有曲线都要自己探究的话,就真是太难了。

莫怕,后面还有一大半的边幅,就是帮你解决这个问题的。

3.常用的运动曲线

世界上是有很多大神的,他们已经研究出一系列常用的动画曲线了。

常用的动画曲线

对此做一下简单的介绍:

  • In和Out:大多数In曲线是从慢到快,可以结合汽车开始跑起来的场景来理解;大多数Out曲线是从快到慢,可以结合汽车慢慢停下来的场景来理解。通常元素飞入时用Out动画,飞出时用In动画,而元素切换时可以用inOut动画(如banner里的图片切换)。如果细心留意一下,你会发现其实Out曲线就是In曲线从右到左运动的轨迹,他们是中心对称的。
  • Quad : x^2,是一条二次方曲线
  • Cubic : x^3,是一条三次方曲线
  • Quart : x^4,是一条四次方曲线
  • Quint: x^5,是一条五次方曲线
  • Sine :sin(x^(pi/2))
  • Expo:2^(10(x-1)),是一个开始非常慢,中后期非常快的曲线
  • Circ:顾名思义就是弧(1/4圆,如果选择了InOut就是两个外切的1/4圆)
  • Bounce:这是个模拟小球落地的反弹曲线
  • elastic:这是个模拟弹簧运动的曲线,就是我们前面研究想得出的曲线

这么多曲线,可能大家一看就晕了。我个人理解,用得比较多的应该是其中的几个:

  1. Quad – x^2:这条二次方曲线,就是匀变速直线运动曲线,大家应该还记得初中背得滚瓜烂熟的s=0.5 * a * t²吧。
    有了匀变速运动曲线,很多现实中的运动都可以模拟了,如匀加速运动摩擦力匀减速运动。如果再组合使用曲线,就能模拟出更多运动了,例如y轴使用二次曲线,x轴使用线性曲线,就模拟出一个平抛动画了。
    月影大神分享过一个ppt,里面列举了一些匀加/减速时的二维运动的动画曲线及实现:戳我戳我

  2. Cubic – x^3:这是条三次方曲线,大家还记得初中物理哪儿用到这条曲线吗?。。。。对了,就是变加速直线运动,如下图:

在此再附一张上面列举的幂函数曲线对比图供参考和使用:

3.elastic曲线:这个就是前面在研究的弹簧曲线。实现了和ios的spring动画相似的效果。

4.Bounce曲线:模拟小球落地效果的曲线。


除此以外,我试过用一个sin曲线设置物体的透明度,实现呼吸灯效果。

在接下来介绍的GreenSock库中,还有一些动画曲线可供使用:

有了这些曲线,我们下一步就是要使用它了,这儿将通过js和css来使用这些曲线:

4.通过js使用动画曲线:

以上这些曲线的函数可以在这个js中找到:戳我戳我

//部分代码展示
var Tween = {
    Linear: function(t, b, c, d) { return c*t/d + b; },
    Quad: {
        easeIn: function(t, b, c, d) {
            return c * (t /= d) * t + b;
        },
        easeOut: function(t, b, c, d) {
            return -c *(t /= d)*(t-2) + b;
        },
        easeInOut: function(t, b, c, d) {
            if ((t /= d / 2) < 1) return c / 2 * t * t + b;
            return -c / 2 * ((--t) * (t-2) - 1) + b;
        }
    },
    Cubic: {
        easeIn: function(t, b, c, d) {
            return c * (t /= d) * t * t + b;
        },
        easeOut: function(t, b, c, d) {
            return c * ((t = t/d - 1) * t * t + 1) + b;
        },
        easeInOut: function(t, b, c, d) {
            if ((t /= d / 2) < 1) return c / 2 * t * t*t + b;
            return c / 2*((t -= 2) * t * t + 2) + b;
        }
    },
    ……

借助这些函数和requestAnimationFrame,我们可以方便地实现曲线效果,如:

var ball = document.getElementById("ball")
var elasticFall = function() {
    var start = 0,
    beginingValue = 0,
    changeValue = 400,
    during = 100;
    var _run= function() {
        start++;
        var top = Tween.Elastic.easeOut(start, beginingValue, changeValue, during);
        ball.style.webkitTransform = "translateY("+top+"px)";
        if(start < during) requestAnimationFrame(_run);
    }
    _run();
};
elasticFall();

效果预览请戳这里

接着我们分析一下这些函数怎样使用。大部分的曲线动画都包含4个入参:

  • t:当前时间
  • b:初始位置
  • c: 结束位置
  • d:运动时间

我们主要关心的就是b、c、d,可以理解为物体用了d毫秒从b变成c。这是不是很像设置css动画时要关心的东西呢。而t是给程序获得当前时间,计算出此时间下对应的值。
有些动画函数,例如弹簧动画函数Elastic,还有a和p参数。经试验,a:影响振幅,p影响来回次数,按 这儿 关于ios弹簧动画的描述,a的设置相当于质量,而p相当于阻尼系数。

如果不想重复造轮子的话,我搜集了2个动画曲线实现库jstween(https://github.com/shrekshrek/jstween)和GreenSock(http://greensock.com/ease-visualizer)推荐给大家使用。两个库都是挺容易上手使用的,而且还扩展了很多功能,例如按运动曲线同时改变多个属性、动画播放时或完成时执行回调函数等。

以让目标通过弹簧效果在2秒内从x轴上400像素位置移动到0像素位置(即通过弹簧效果从屏幕外移到屏幕内)为例,举个栗子:

//通过jstween实现
JT.fromTo($target, 2, 
{
    x:400      
}, {
    x:0,
    ease: JT.Elastic.Out,
    onEnd: function (n) {
        console.log("animate end.")
    }
});

//通过GreenSock实现
TweenLite.fromTo($target, 2, 
{
    x: '400px'
}, 
{
    x: '0', 
    ease: Elastic.easeOut.config(0.5, 0.4),
    onComplete:function(){
        console.log("animate end.")
    }
}
);

在库选用方面考虑,如果想要轻量的,可以选择jstween,只要14k。而GreenSock相对重量一些(最少得引入TweenLite.min.js、EasePack.min.js、CSSPlugin.min.js,共74k),但他提供了更多的运动曲线可供选择,而且还提供其中一些曲线的参数设置,如可以设置弹簧曲线的物体质量和阻尼系数,这是tweenjs所没有的。此外GreenSock还提供了一个在线调节参数预览效果的页面。大家可以根据需要选用合适的库来实现效果。

GreenSock的运动曲线效果预览器

5.使用css实现曲线动画效果

我们也可以把这些运动曲线运用到CSS Animation的@keyframes中。以下还是以让目标通过弹簧效果从x轴上400像素位置移动到0像素位置为例,使用Sass来做:

//引入函数库 https://github.com/terkel/mathsass,实现sin,cos等数学函数
@import "node_modules/mathsass/dist/math";

//编写弹簧曲线函数
@function elasticAniFn($t) {
    @return -0.5 * pow(exp(1), (-6 * $t)) * (-2 * pow(exp(1), (6 * $t)) + sin(12 * $t) + 2 * cos(12 * $t))
}

//编写物体位移随时间变化的函数
//$b: 初始值
//$c: 变化量
//$p: 当前运动的进度百分比
//可以理解为物体从$b运动到$c,$p用来表示当前运动了 $p%
@function aniFn($b, $c, $p) {
    @return $b + $p * ($c - $b);
}

//声明动画
//由此生成的css:
//@keyframes moveAni {
//0% {
//  transform: translateX(400px);
//}
//1% {
//  transform: translateX(396.54493px);
//}
//2% {
//  transform: translateX(386.76446px);
//}
//3% {
//  transform: translateX(371.53953px);
//}
@keyframes moveAni {
    @for $i from 0 through 100 {
        {$i}% { 
            transform:translateX(aniFn(400px, 0, elasticAniFn($i / 100)));
        }
    }
}

//使用动画
.box {
  animation: 1s moveAni linear;
  transform: rotate
}

6.可视化实现工具介绍

在此我推荐Stylie,一个可视化调节运动曲线且自动生成CSS的工具。

如图所示,左边是动画预览,白色小球会按照设置的曲线不停运动,下方是时间进度条,右边是设置面板。通过可视化地给小球设置每个时间节点上的状态及状态变化时过渡的运动曲线来实现动画效果。

对设置面板做一下简单说明:
1.第一个0ms处表示开始节点时的状态,第二个1000ms处表示1000ms处时间节点的状态,可以点击它来修改时间。点击右上角的加号可以添加新的时间节点。
2.x和y分别表示translateX和translateY,即横坐标及纵坐标,不过一般我会直接拖动左边的绿色十字来调整位置;s表示scale,即缩放倍率;rX、rY、rZ表示rotateX、rotateY、rotateZ,即绕X、Y、Z轴的旋转角度;每个状态右边都可以选择运动曲线,如linear是线性运动曲线,bounce是小球落地的运动曲线。

调整满意后就可以导出代码了:

Orient generated animation to是说所有的位移数值采用相对(第一帧的)位移,还是绝对定位(相对于左上角)。class name处可以修改动画的类名,vendors处可以添加需要的浏览器前缀(一般勾选WebKit和W3C就好了)。

大概就是这样了,这个工具基本上可以解决很多CSS动画需求了,具体做得怎样就看各人的功力了。

总结

除了基本的css动画函数,我们还可以用更丰富自然的曲线函数去模拟物体的运动。在使用场景上,如果不介意库体积、想有丰富的曲线函数供使用,可以用GreenSock来实现;对库体积有要求,可以用jstween来实现;觉得只想实现其中一个动画效果而觉得没必要引入整个库,可以只使用其动画函数和requestAnimationFrame来实现;不想用js实现(UI开发工程师的骄傲),可以用sass+动画函数来实现;想所见即所得,可以用Stylie来实现。而遇到比较特别的动画效果,不能用前面列举的动画函数来实现,就只能通过研究物体运动背后的运动曲线实现了。希望大家看完之后能有所收获,撒花~

参考文献:
[1] Thai Pangsakulyanont.Spring Animation in CSS
[2]阿布evo.可视化CSS3动画生成神器 - Stylie

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,617评论 4 59
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 师父领进门,修行靠个人。通过短短两周对超级行动课的学习,我的学习思维发生了彻底的变化,或者说我以前是压根没有...
    猫月无痕阅读 310评论 4 6
  • 〔一〕 一袭凉风 帘儿 半卷 半舒 我,,,我,,, 倚窗静听 那远处的琴声 像是一场海崩 淹没我的心滨 谁还...
    风之诺言_919c阅读 331评论 0 0
  • Python.Boost是让Python能够更直接自由的调用C++函数和类的库。这个库的产生是为了提高Python...
    fromradio阅读 1,709评论 0 6