Animation
出自OpenFace
第十七章 动画
第三部分. 基本概念
目录 |
1. 介绍
OpenFace应用让用户体验到有一种流动的,动态的感觉.这种流动行是通过在指定的时间段改变对象的属性值的的动画实现的。本章我们会通过简短的应用的代码来展示使用动画时简单的本质。后面会有写复杂的交互的例子是您在活动中看到动画的效果。
例如,下面的应用为view定义了动画,使它在1秒内运动到‘x’是100的位置。animator是类LzAnimator的实例,并且通过使用<animator>标签来定义。
Example 17.1. 简单动画
<canvas height="100" width="500"> <view width="50" height="50" bgcolor="red"> <animator attribute="x" to="100" duration="1000"/> </view> </canvas>
动画由当前位置开始运动,除非有特别的设置。下例应用为view定义了一个在1秒内首先移动到‘x’为50的位置,继而再移动到‘x’是100的位置的动画。
Example 17.2. 制定启动条件
<canvas height="100" width="500"> <view x="50" width="50" height="50" bgcolor="red"> <animator attribute="x" from="50" to="100" duration="1000"/> </view> </canvas>
动画在默认的情况下通过变速改变对象的属性值从‘from’到‘to’。运行上例会展示一个变速移动的视图。该非线性行为可以通过使用motion属性得到最小限度的控制。例如,下例展示view线性的在屏幕上移动的效果,和上例形成对比。motion属性的取值可以是:开始慢并减速,开始慢并加速,开始快并减速,匀速。
Example 17.3. 线形运动
<canvas height="100" width="500"> <view x="50" width="50" height="50" bgcolor="red"> <animator attribute="x" to="100" duration="1000" motion="linear"/> </view> </canvas>
动画像视图那样可以命名以便于用脚本语言来操作。下例中展示了按任意键来激活动画,而不是初始时实例动画。可以通过设置start 属性值为false 初始动画为静止状态,继而通过调用start()方法来激活动画。
Example 17.4. 使用脚本控制动画
<canvas height="100" width="500">
<view bgcolor="red" width="100" height="100" oninit="LzFocus.setFocus(this)">
<handler name="onkeydown" reference="LzKeys" args="k">
var key=k.intValue();
this.myAnimator.doStart();
</handler>
<animator name="myAnimator" attribute="x" to="100" duration="1000" start="false"/>
</view>
</canvas>
动画也可以相对他们当前的位置来移动对象。这意味着目的位置实际是‘to’的值加上对象的当前值。运行上面的例子并设置relative属性的值是true,创建一个每次按任意键都向右移动100像素的视图。
Example 17.5. 相对动画
<canvas height="100" width="500">
<view bgcolor="red" width="100" height="100" oninit="LzFocus.setFocus(this)">
<handler name="onkeydown" reference="LzKeys" args="k">
var key=k.intValue();
this.myAnimator.doStart();
</handler>
<animator name="myAnimator" attribute="x" to="100" duration="1000" start="false" relative="true"/>
</view>
</canvas>
可以给一个对象添加多个动画,并且使他们同时奏效。下面的代码展示了一个对象在‘x’,‘y’两个方向的动画同时作用下对角移动的效果。
<canvas height="100" width="500"> <view bgcolor="red" width="100" height="100" > <animator attribute="x" to="100" duration="1000"/> <animator attribute="y" to="100" duration="1000"/> </view> </canvas>
为了处理两个动画,可以使用例如下面的<animatorgroup>标签而实现的相继而行。
浏览例子的比较
下例说明使用<animator> and <animatorgroup>标签的各种使用方法。使用这些例子来比较不同动画技术间的不同。 下面的例子展示了简单视图的透明度,旋转度,高度,宽度,横纵坐标值的动画效果。
Example 17.6. 基本动画
<canvas width="800" height="200" bgcolor="0xE5E1B0" >
<view id="tutorial1" height="600" width="${parent.width}" visible="true">
<ruler name="rlr" height="3000" y="0"/>
<view height="30"/>
<view height="120" y="30">
<view fontsize="15">
<text text="opacity"/>
<text text="rotation"/>
<text text="width"/>
<text text="height"/>
<text text="x"/>
<text text="y"/>
<simplelayout axis="x" />
</view>
<view y="20" fontsize="20">
<simplelayout axis="y" />
<view id="B1" x="50" width="50" height="50" bgcolor="red">
<animator name="anm" attribute="opacity" to="0.3" duration="500" start="false"/>
</view>
<text id="B2" x="50" text="rotation" >
<animator name="anm" attribute="rotation" to="45" duration="500" start="false"/>
</text >
<text id="B3" x="50" text="width">
<animator name="anm" attribute="width" to="99" duration="500" start="false"/>
</text >
<text id="B4" x="50" text="height">
<animator name="anm" attribute="height" to="100" duration="500" start="false"/>
</text >
<text id="B5" x="50" text="x">
<animator name="anm" attribute="x" to="500" duration="500" start="false"/>
</text >
<text id="B6" x="50" text="y">
<animator name="anm" attribute="y" to="50" duration="500" start="false"/>
</text >
</view>
<simplelayout axis="y" />
</view>
<view x="50" y="150" width="${parent.width - 50}">
<text name="Test" x="50" oninit="LzFocus.setFocus(this)" >
<handler name="onkeydown" reference="LzKeys">
B1.anm.doStart();
B2.anm.doStart();
B3.anm.doStart();
B4.anm.doStart();
B5.anm.doStart();
B6.anm.doStart();
</handler>
</text>
</view>
</canvas>
2. 绝对和相对动画
下例展示了动画相对移动和绝对移动的区别。默认的情况,相对移动设置relative属性值为false,意思是要计算出相对于动画属性的初始值。当relative的值为true时,动画被触发后它的移动和该属性(relative)的初始值是有关的。
Example 17.7. 绝对和相对
<canvas width="800" height="200" bgcolor="0xE5E1B0" >
<view x="10" y="70" width="50" height="50" bgcolor="red">
<text fontsize="15">relative=true</text>
<animator attribute="x" to="200" duration="1000" relative="true"/>
</view>
<view x="10" y="150" width="50" height="50" bgcolor="red">
<text fontsize="15">relative=false</text>
<animator attribute="x" to="200" duration="1000" relative="false"/>
</view>
</canvas>
3. 使用from 属性
属性from指明动画开始移动的起始值。若对象不在该位置,择假定它在那个位置,之后开始移动。下例中,要移动的属性是‘x’,水平方向。‘from’值是200,‘to’值是100。动画开始前,对象首先设置‘x’属性值为100。在绝对移动情况下,对象移动到‘x’为100,也就是计算出的相对对象的初始值x位置。
Example 17.8. 使用from 属性
<?xml version="1.0" encoding="utf-8" ?>
<canvas>
<text text="repeate=3" fontsize="20" x="0" y="50" name="rep" multiline="true" width="100">
<animator attribute="x" from="200" to="100" duration="1000" repeat="3" />
</text>
<text text="relative" fontsize="20" x="0" y="100" multiline="true" width="150">
<animator attribute="x" from="200" to="100" duration="1000" relative="true" />
</text>
</canvas>
4. 使用motion属性
属性motion的值可以指定为:加速,减速,加速再减速,匀速。
Example 17.9. 使用motion属性
<?xml version="1.0" encoding="utf-8" ?>
<canvas height="320" width="240" >
<view x="30" y="10" width="50" height="50" bgcolor="red" name="v">
<text fontsize="15" name="t1" >motion=linear</text>
<animator attribute="x" from="0" to="160" duration="1000" motion="linear"/>
</view>
<view x="30" y="40" width="50" height="50" bgcolor="blue">
<text fontsize="15" name="t1" >motion=easein</text>
<animator attribute="x" from="0" to="160" duration="1000" motion="easein"/>
</view>
<view x="30" y="70" width="50" height="50" bgcolor="green">
<text fontsize="15" name="t1" >motion=easeout</text>
<animator attribute="x" from="0" to="160" duration="1000" motion="easeout"/>
</view>
<view x="30" y="110" width="50" height="50" bgcolor="red">
<text fontsize="15" name="t1" >motion=easeboth</text>
<animator attribute="x" from="0" to="160" duration="1000" motion="easeboth"/>
</view>
</canvas>
5. 标签和脚本
在OpenFace应用程序中可以通过使用XML标签或JavaScript APIs打到同样的效果。上例已说明怎样使用<animator>标签,下例对比标签和脚本近似的动画效果。
Example 17.10. 反弹效应
<?xml version="1.0" encoding="utf-8" ?>
<canvas height="320" width="240" >
<view x="30" y="140" width="50" height="50" bgcolor="red" name="v1">
<text fontsize="15">setMotion(easein)</text>
<method name="st">
this.myAnimator.setMotion("easein");
this.myAnimator.doStart();
</method>
<animator name="myAnimator" attribute="x" from="0" to="160" duration="1000" start="false"/>
</view>
<view x="20" y="180" width="50" height="30" bgcolor="red" name="v2">
<text fontsize="15">xsetTo(160)</text>
<animator name="myAnimator" attribute="x" duration="1000" start="false"/>
<method name="st">
this.myAnimator.setTo(160);
this.myAnimator.doStart();
</method>
</view>
<view x="20" y="220" width="30" height="30" bgcolor="green" opacity="0" name="v3">
<text fontsize="15">opacity setTo(1)</text>
<animator name="myAnimator" attribute="opacity" duration="1000" start="false"/>
<method name="st">
this.myAnimator.setTo(1);
this.myAnimator.doStart();
</method>
</view>
<handler name="onkeydown" reference="LzKeys"><![CDATA[
canvas.v1.st();
canvas.v2.st();
canvas.v3.st();
]]></handler>
</canvas>
6. 使用indirect 属性
indirect 属性可以从反向使动画得到同样的结果。下例中,将第二个动画的indirect 属性设置为true。这使它向左移动,而不向右,从画布中消失了,继而象应该的那样它又从右侧出现了。在动画中设置inderect属性为true,rotation属性使运动由顺时针改变为逆时针。试着在给其他诸如:height,width,opacity属性设置动画时改变inderect属性后动画的效果。
Example 17.11. Indirect 属性
<?xml version="1.0" encoding="utf-8" ?>
<canvas height="320" width="240" >
<view x="10" y="10" resource="image/15.gif" name="v0">
<text fontsize="15">indirect=false</text>
<animator attribute="x" from="0" to="200" duration="1000" indirect="false"/>
</view>
<view x="10" y="40" resource="image/15.gif">
<text fontsize="15">indirect=true</text>
<animator attribute="x" from="0" to="200" duration="1000" indirect="true"/>
</view>
</canvas>
7. 动画组
<animatorgroup>标签允许对一个对象添加多个动画行为。动画可以设置成同步或异步奏效。下例中,动画组被用于两个红色视图的的‘x’和‘y’属性。 第一个animator的x属性的motion设置为减速,而不是默认的先加速再减速,第二个animator的y属性的motion设置为加速。 <animatorgroup>的process属性值允许设置为同步和异步两种。默认情况是同步,为了看两者的区别,可以修改下例中的process属性值为"sequential"(异步)。
Example 17.12. 动画组
<?xml version="1.0" encoding="utf-8" ?>
<canvas height="320" width="240" >
<drawview width="200" height="200" name="d1">
<handler name="oninit">
var g = this.createLinearGradient(0,0,75,75)
this.globalAlpha = 0;
g.addColorStop(0, 0x000000);
// this.globalAlpha = 1;
g.addColorStop(1, 0xffffff);
this.fillStyle = g;
this.fill();
this.moveTo(100, 100);
this.lineTo(100, 200);
this.quadraticCurveTo(150, 250, 200, 200);
this.closePath();
this.fillStyle = 0x0000ff;
// this.globalAlpha = .5;
this.fill();
this.strokeStyle = 0xffff00;
this.lineWidth = 5;
this.stroke();
</handler>
<animator attribute="x" from ="0" to="200" duration="1000" />
<animatorgroup process="simultaneous">
<animator attribute="opacity" to="0" duration="2000" />
<animator attribute="width" to="0" duration="2000" />
<handler name="onstop">
this.parent.a1.doStart();
</handler>
</animatorgroup>
<animatorgroup start="false" name="a1" process="simultaneous">
<animator attribute="opacity" to="1" duration="2000" />
<animator attribute="width" to="50" duration="2000" />
<animatorgroup duration="1000">
<animator attribute="rotation" to="-50" />
<animator attribute="x" to="10"/>
<animator attribute="y" to="100"/>
</animatorgroup>
<handler name="onstop">
canvas.v1.sqr.stop();
this.setTarget(canvas.v1);
this.doStart();
// canvas.v1.sqr.stop();
</handler>
</animatorgroup>
</drawview>
<view bgcolor="red" x="${this.sqrtx*this.sqrtx}" y="${canvas.d1.y+50}" width="50" height="50" name="v1">
<attribute name="sqrtx" value="0" />
<animatorgroup duration="5000">
<animator name="sqr" attribute="sqrtx" from="0" to="12" duration="2000" motion="easein"/>
<animator attribute="rotation" to="360" motion="easeout"/>
</animatorgroup>
</view>
</canvas>
动画组中还可以包含动画组,即允许封装这一复杂的行为。下例中说明使view返回它的起始点。
起始的分别移动后沿X和Y轴同步移动对象。
Example 17.13. 动画组-2
<canvas height="210">
<view bgcolor="red" width="100" height="100" oninit="LzFocus.setFocus(this)">
<text align="center" valign="middle">input key!</text>
<animatorgroup name="outeranimatorgroup" process="sequential" start="false">
<animator attribute="x" to="100" duration="1000"/>
<animator attribute="y" to="100" duration="1000"/>
<animatorgroup process="simultaneous" duration="1000">
<animator attribute="x" to="0"/>
<animator attribute="y" to="0"/>
</animatorgroup>
</animatorgroup>
<handler name="onkeydown" reference="LzKeys" >
this.outeranimatorgroup.doStart();
</handler>
</view>
</canvas>
需要注意上面代码中内部的<animatorgroup>没有给‘x’,‘y’动画设置duration属性.duration, x, y, width等属性可以在<animatorgroup>中定义,并且这些值可以被子动画使用,除非子动画已定义了这些属性。上例中,duration的值被赋给动画组,不意味这animatorgroup将仅限于这个段时间。意思是每个子动画都可以使用该属性。若process属性值为‘simultaneous’并且每个子动画都使用组的duration属性作为自己的值,那么所有子动画的总时间长度就等于animatorgroup的duration值。
8. 动画布局
您可以修改任何在运行时能修改的动画属性。例如,下例展示了您移动布局的动画效果。
Example 17.14. 动画布局
<canvas width="800" height="185" bgcolor="0xE5E1B0">
<view id="tutorial2" height="200" width="${parent.width}" visible="true">
<ruler name="rlr" height="150" y="0"/>
<view height="30"/>
<view height="60" y="40">
<view id="V01" x="50">
<view width="30" height="30" bgcolor="red"/>
<view width="30" height="30" bgcolor="red"/>
<view width="30" height="30" bgcolor="red"/>
<view width="30" height="30" bgcolor="red"/>
<simplelayout name="lyt" axis="x" spacing="5">
<animatorgroup name="anm" start="false" process="sequential">
<animator attribute="spacing" from="5" to="50" duration="1000"/>
<animator attribute="spacing" from="50" to="-49" duration="1000"/>
<animator attribute="spacing" from="-49" to="5" duration="1000"/>
</animatorgroup>
</simplelayout>
</view>
</view>
<view x="50" y="150" >
<text text="Test" x="50" cap="both" oninit="LzFocus.setFocus(this)" >
<handler name="onkeydown" reference="LzKeys">
V01.lyt.anm.doStart();
</handler>
</text>
</view>
</view>
</canvas>
9. 万物动画
既然已经了解动画如何使用,那么就可以探索动画的各不同属性的使用了。提示:试着将view隐藏,用charm约束其他属性的值,之后对charm使用动画。
例如,很多时候,你需要有反复的动画。这可能是一大麻烦,因为很明显的方式做它需要两个动画,简单的做法是在一个动画组中调整属性实现。
一个简单的变通方法是用带动画价值的平方根目的值。这也能实现使用单个动画反复移动的行为。
Example 17.15. 单一动画的复合行为
<canvas height="50">
<view width="30" height="30" bgcolor="red" x="${225-(this.sqrtx * this.sqrtx)}">
<attribute name="sqrtx" value="-15"/>
<animator attribute="sqrtx" from ="-15" to="15" duration="3000" repeat="Infinity"/>
</view>
</canvas>

