一、 简介
在探讨微软ASP.NET Ajax框架的过程中,我总结出这个框架在网页动画方面的支持可分为以下两种类型:第一部分是依托于Futures CTP部分的跨浏览器兼容的低级动画技术;第二部分是依赖于ASP.NET 2.0服务器端技术的ASP.NET AJAX Control Toolkit部分。很明显,这两种方案各存利弊。在本文中,我将专注于向读者介绍第一种方案,即“跨浏览器兼容的低级动画技术”,并针对每种动画给出相应的示例网页程序。
【注】本文示例程序试验环境为:Windows XP Professional+IIS 6.0+Visual Studio 2005+ASP.NET AJAX框架的前两部分(即ASP.NET AJAX Essential Components和ASP.NET AJAX Futures May CTP),而第三部分ASP.NET AJAX Control Toolkit是可选安装的;但本文强烈建议读者试安装之,因为这一部分中包含了前面提到的完整的第二种方案动画技术。
二、 网页动画概述
网页中的动画是我们司空见惯的,合理地在页面中运用动画效果可以给用户以相当震撼的视觉冲击,并牢牢地抓住用户的眼球。在网页中实现动画的方式多种多样,Flash就是其中的佼佼者。对于大型网页动画设计而言,Flash应是首当其冲的选择工具;但对于普通开发中某些常见的、小巧的动画效果,例如删除列表某一行之后该行以淡出的效果消失等,使用Flash则犹如杀鸡用宰牛刀—笨得很。
针对这种情形,ASP.NET AJAX也当仁不让,在其Futures CTP部分中提供了许多种内置的动画技术支持。所有这些动画都定义于一个叫PreviewGlitz.js的文件内(此文件内置于Futures CTP程序集Microsoft.Web.Preview.dll中)。
为了更为清晰地区别本文中我们所学习的各种动画间的关系,下图1是我从脚本文件PreviewGlitz.js中提取出的各种动画类间的继承关系图。
图1—Futures CTP提供的各种动画类间的继承关系图。
从上图中看出,Animation是所有MS AJAX客户端动画类的基类。尽管PropertyAnimation和InterpolatedAnimation这两个类也是派生自Animation类的子类,但一般情况下,它们仍作为基类进一步派生子类使用而不单独用于创建动画。
三、 创建淡入/淡出动画效果
借助于FadeAnimation动画类,我们可以实现使页面中的某个层的不透明度逐渐变为1(淡入)或变为0(淡出)。
请启动Visual Studio 2005,然后选择菜单项“文件|新建网站…”,使用模板“ASP.NET AJAX CTP-Enabled网站”创建一个新的网站,并命名工程为GlitzTest(选择Visual C#作为内置语言)。此后,系统应该自动地添加对必要的程序集—System.Web.Extension.dll(你无法直接在bin目录下看到它)和Microsoft.Web.Preview.dll的参考。
此外,你会注意到一个ScriptManager服务器控件自动地添加到页面中。注意,这个服务器控件作为整个ASP.NET AJAX框架的控制中心。
【注意】由于本文中的动画类都存在于一个外部库中,所以,我们必须以手工方式把文件PreviewGlitz.js包含到任何使用它们的网页的<asp:ScriptManager>控件的<Scripts>子节下面。此外,我们还要以同样方式手工包含文件—PreviewScript.js(这个文件是整个Futures CTP的核心)。此外,为了简单起见,我把本文中所有示例页面纳入到一个示例工程GlitzTest下。
然后,右击工程GlitzTest添加一个新网页并命名为AjaxFadeAnimation.aspx。稍试修改,你会得到如下面图2所示的设计时刻快照。
图2—淡入/淡出动画演示网页设计时刻快照。
下面,首先让我们来看这个网页相应的HTML代码部分。
首先,关于</asp:ScriptManager>节中的内容前面已经提过。接下来,我们定义了两个按钮(相应的id值分别为btnFadeOutAnimate和btnFadeInAnimate),并且分别把它们与自己的onclick事件处理器关联起来。后面跟着的是一个<img>标签,用于测试淡入/淡出效果之用。读者可能已经猜出其中的关键在于这两个事件处理器函数。不错!下面就让我们对其进行深入的分析。<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Assembly="Microsoft.Web.Preview" Name="PreviewGlitz.js" />
<asp:ScriptReference Assembly="Microsoft.Web.Preview" Name="PreviewScript.js" />
</Scripts>
</asp:ScriptManager>
<br />
<div class="h1">Fading in and out Animation Demo</div><br />
<input id="btnFadeOutAnimate" type="button" value="Fade Out" style="width: 155px; height: 37px" language="javascript" onclick="return FadeUsingFutures(true)" />
<input id="btnFadeInAnimate" style="width: 135px; height: 37px" type="button" value="Fade In" language="javascript" onclick="return FadeUsingFutures(false)" /><br />
<div id="animationTarget" style="width: 207px; height: 252px; background-color: #ffcc00">
<img src="img/lushan_1.jpg" style="width: 208px; height: 258px;" />
</div>
</form>
<script language="javascript" type="text/javascript">在这段代码中,我们首先获取一个到动画目标的句柄(或指针)。然后,创建动画类FadeAnimation的一个实例fadeAnimation。然后,设置这个实例的相应的常用动画参数,例如持续时间,这个动画实例针对的目标对象,最大不透明度,播放速率(帧/秒)等。至此,我们为动画播放作好了全面的准备。然后,当用户点击按钮“Fade Out”(或“Fade In”)时,事件处理器函数FadeUsingFutures被激发,根据传入参数确定播放类型,最后正式进行动画播放。
var domElementVar = new Sys.UI.Control( $get("animationTarget") );
var fadeAnimation = new Sys.Preview.UI.Effects.FadeAnimation();
fadeAnimation.set_duration( 0.3 );
fadeAnimation.set_target( domElementVar );
fadeAnimation.setValue( 70 );
fadeAnimation.set_fps(45);
function FadeUsingFutures( fadeOut )
{
var fadeEffect = fadeOut ? Sys.Preview.UI.Effects.FadeEffect.FadeOut : Sys.Preview.UI.Effects.FadeEffect.FadeIn ;
$get("btnFadeOutAnimate").style.display = fadeOut ? "none":"block";
$get("btnFadeInAnimate").style.display = fadeOut ? "block":"none";
fadeAnimation.set_effect( fadeEffect );
fadeAnimation.play();
}
</script>
四、 创建长度动画效果
接下来,我们来学习另一种类型的动画—LengthAnimation。这种动画可以用来在某个开始和结束的范围内连续地改变某个属性值。这种动画的典型应用就是逐渐改变某个控件的长度和宽度属性值。
注意,虽然这种LengthAnimation动画用来改变某个数值类型的属性值十分容易,只需要指定某开始值和结束值即可,但它也同样可以用来连续地改变文本类型的属性值。
为了进一步了解这种动画的实现细节,下面表格列举了这种动画的一些常用的属性。
属性
|
描述
|
target
|
指定页面上将要应用该动画的元素的id
|
property
|
指定该动画将应用到页面元素中的哪个属性上
|
startValue
|
指定该动画将改变的值范围的开始值
|
endValue
|
指定该动画将改变的值范围的结束值
|
unit
|
指定该动画将改变的属性的单位。例如像素单位使用px,百分比单位使用%
|
duration
|
指定该动画将运行的时间长度,单位为秒
|
fps
|
获取或设置该动画的fps属性。默认值为25
|
isActive
|
获取一个布尔值,代表该动画是否已经开始运行
|
isPlaying
|
获取一个布尔值,代表该动画是否正在运行
|
percentComplete
|
获取一上0到100的数字,代表该动画目前完成运行的百分比
|
表格1—动画LengthAnimation常用属性。
下面,让我们来创建一个简单的长度动画。
如上面一样,仍然是右击工程GlitzTest添加一个新网页并命名为LengthAnimation.aspx。稍加修改,你会得到如下面图3所示的设计时刻快照。
图3—长度动画演示网页设计时刻快照。
下面的图4则展示了动画过程中的某一时刻的屏幕快照。
图4—长度动画演示过程中某一时刻的屏幕快照。
你可能已经猜出,动画的最后结果将显示一张最大的图片。
下面,让我们深入分析其中的逻辑。下面是代码相应于页面LengthAnimation.aspx的HTML代码部分。
//……省略
<img id="i" src="img\girl1.jpg" width="100" />
<hr />
<input type="button" id="startButton" value="Start"/>
注意,在此我们首先使用<img>标签创建了一幅图画,并直接指定其初始宽度值为100,而不是使用style属性指定其宽度值—这对于后面将改变我们的动画目标的属性width而言是极其重要的。后面的按钮定义不再赘述。
接下来,让我们分析真正吸引我们的相关xml-script编程部分,如下所示:
【注】本人在实验中发现一件奇怪的事情:在上面的编程中,如果我们不引入行为LayoutBehavior作为“中介”,在点击按钮Start时只能导致屏幕上的图片一下子消失。这是否是一个“bug”?对于动画LengthAnimation而言,目前实在没有太多的参考资料,只能靠我们自己试验,试验,再试验……
下面,让我们来创建一个简单的长度动画。
如上面一样,仍然是右击工程GlitzTest添加一个新网页并命名为LengthAnimation.aspx。稍加修改,你会得到如下面图3所示的设计时刻快照。
图3—长度动画演示网页设计时刻快照。
下面的图4则展示了动画过程中的某一时刻的屏幕快照。
图4—长度动画演示过程中某一时刻的屏幕快照。
你可能已经猜出,动画的最后结果将显示一张最大的图片。
下面,让我们深入分析其中的逻辑。下面是代码相应于页面LengthAnimation.aspx的HTML代码部分。
//……省略
<img id="i" src="img\girl1.jpg" width="100" />
<hr />
<input type="button" id="startButton" value="Start"/>
注意,在此我们首先使用<img>标签创建了一幅图画,并直接指定其初始宽度值为100,而不是使用style属性指定其宽度值—这对于后面将改变我们的动画目标的属性width而言是极其重要的。后面的按钮定义不再赘述。
接下来,让我们分析真正吸引我们的相关xml-script编程部分,如下所示:
在此,我们首先使用<image>标签创建了一个Sys.Preview.UI.Image的实例(使其指向id为i的实际的HTML<img>元素)。然后,我们定义了一个LayoutBehavior(其id为“Label1Style”),并把它绑定到上面的图像实例上。注意,这个LayoutBehavior行为将用作后面定义的LengthAnimation动画的目标。通过连续地改变行为Label1Style的宽度(从100px改变到480px),使图像的尺寸发生相应的改变。注意,在此将属性duration的值设置得越小,图像变大的速度将越快。此外,如果把属性startValue的值设置得比endValue的值还大,则LengthAnimation动画将以相反的方向执行,即从属性startValue减小到endValue,表现为图像由原来的较大变得逐渐收缩。<script type="text/xml-script">
<page xmlns:script="http://schemas.microsoft.com/xml-script/2005">
<components>
<image id="i">
<behaviors>
<LayoutBehavior id="Label1Style" />
</behaviors>
</image>
<lengthAnimation id="lani"
target="Label1Style" property="width"
startValue="100" endValue="480" fps='25'
duration="5" >
</lengthAnimation>
<button id="startButton">
<click>
<InvokeMethodAction target="lani" method="play" />
</click>
</button>
</components>
</page>
</script>
【注】本人在实验中发现一件奇怪的事情:在上面的编程中,如果我们不引入行为LayoutBehavior作为“中介”,在点击按钮Start时只能导致屏幕上的图片一下子消失。这是否是一个“bug”?对于动画LengthAnimation而言,目前实在没有太多的参考资料,只能靠我们自己试验,试验,再试验……
五、 创建数字动画效果
这个NumberAnimation动画的特征十分类似于LengthAnimation(具有相同的属性,例如target,property,startValue,endValue,duration,fps,isActive,isPlaying,percentComplete,等等);不过,也存在两处重要的区别。其一是,动画NumberAnimation的中间值在改变时可以带有小数(通过属性integralValues来设置实现)。于是,在某些特定场合下(例如对于长度单位米的处理),使用动画NumberAnimation将会带来更好的效果。第二个区别在于,动画LengthAnimation支持基于像素的动画的改变(通过属性unit来实现),而动画NumberAnimation却不是这样。
现在,让我们来看一个模拟倒计时的直观的例子。
仍然使用鼠标右键单击工程GlitzTest并且添加一个新的网页,并命名为NumberAnimation2.aspx(注意,在本文示例工程GlitzTest中,我还提供了另一个展示动画NumberAnimation功能的例子—对应网页NumberAnimation.aspx。此动画的目标是通过逐渐改变行为OpacityBehavior的属性值来模拟淡入/淡出动画效果。具体实现请参考下载源码)。下列的图5展示了页面NumberAnimation2.aspx的设计时刻屏幕快照。
图5—数字动画演示网页设计时刻快照。
在最开始,一个整数30显示于屏幕之上。随着把属性integralValues设置为false和动画过程的不断进行,屏幕上逐渐出现如图6所示的小数的情形。
图6—倒计时过程中屏幕出现小数的情形。
在这个例子中,我们在屏幕上放置了一个<span>元素用于表现要倒计时的内容,还有一个按钮来触发该动画过程。篇幅所限,我们仍然只讨论如下所示的xml-script编程部分:
//……省略
<components>
<label id="mynumber" />
<numberAnimation id="mynumberAnimation"
target="mynumber" property="text"
startValue="30" endValue="0" integralValues="false" duration="30" />
<button id="startButton">
<click>
<InvokeMethodAction target="mynumberAnimation" method="play" />
</click>
</button>
</components>
在上面的脚本中,我们首先把一个名字为mynumber的<span>元素与一个MS AJAX客户端Label控件关联起来。注意,在此控件Label提供了一个名为text的属性,这个属性值的改变将显示于对应的<span>元素内。接下来,创建了一个名为numberAnimation的结点—其属性target指向标签mynumber,property设置为text,从而使动画的内容在控件mynumber中得以改变。然后,我们分别把两个相关的属性startValue和endValue设置为30和0。通过把属性integralValues的值设置为false,网页才能显示出小数部分;否则(设置为true的话),我们将看到一个倒计时的秒表效果的动画。
总的来说,借助于动画NumberAnimation,我们可以非常容易地连续不断地改变某个值,并把这一过程与页面中的某个元素关联起来。
六、 创建离散动画效果
现在,让我们讨论另外一种类型的动画—DiscreteAnimation。首先,在这种动画与前面讨论的两种动画LengthAnimation和NumberAnimation之间依然存在某些相似性,例如同样可以在某个特定的时间内依次给出某个范围内的值。不过,对于动画LengthAnimation和NumberAnimation而言,只需给出开始值和结束值即可,由MS AJAX框架负责计算所有的中间值。但对于离散动画而言,我们必须给出动画过程中的所有中间值,例如26个英文字母的列表或一串单词的列表等。然后,在动画播放过程中,由DiscreteAnimation实时地播放出这个序列中的每一个值。这种动画也支持例如target,property,duration,fps,isActive,isPlaying,percentComplete等属性,但还提供了一个特有的属性—values。这个values属性用于指定动画过程中的所有中间值,并用逗号将所有可选值连接起来,组成一个字符串。
下面,让我们创建一个利用离散技术创建动画效果的例子。仍然使用鼠标右键单击工程GlitzTest并且添加一个新的网页,并命名为discrete.aspx。下列的图7展示了示例页面的设计时刻屏幕快照。
图7—离散动画演示网页设计时刻快照。
如果你按下F5运行这个例子,那么,你将看到一个离散动画过程:屏幕中的文本标签内容依次从“Sunday”改变到“Saturday”。在此,我们依然只讨论相应的xml-script编码部分。
<script type="text/xml-script">
<page xmlns:demo="demo">
<components>
<label id="sampleLabel" />
<discreteAnimation id="wordAnimation"
target="sampleLabel" property="text"
values="'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'"
duration="3" />
<button id="startButton">
<click>
<invokeMethodAction target="wordAnimation" method="play" />
</click>
</button>
</components>
</page>
</script>
在上面的脚本中,我们首先把一个名为sampleLabel的<span>元素与一个MS AJAX客户端Label控件建立关联(此控件将对应离散动画的目标元素)。然后,创建了一个名为discreteAnimation的结点,其属性target指向标签sampleLabel,属性property指向控件sampleLabel的text属性。当然,这里最重要的属性还是values,它的值是一个由多个可选元素组成并以逗号间隔的字符串。读者应该猜出,如果我们在此指定一串整数,则完全可以模拟一个倒计时秒表的效果。仅此而已。
七、 创建高级复合动画效果
很显然,仅从字面上,读者就能够猜出,这种类型的动画是用于创建更复杂动画的“包装物”(或“容器”)。的确如此。现在,让我们首先来观察一下文件PreviewGlitz.js内复合动画CompositeAnimation相应的源码(因为相当精简):
Sys.Preview.UI.Effects.CompositeAnimation=function(){
Sys.Preview.UI.Effects.CompositeAnimation.initializeBase(this);
this._animations=Sys.Component.createCollection(this)
};
Sys.Preview.UI.Effects.CompositeAnimation.prototype={
get_animations:function(){
return this._animations},
getAnimatedValue:function(){
throw Error.invalidOperation()},
dispose:function(){
this._animations.dispose();
this._animations=null;
Sys.Preview.UI.Effects.CompositeAnimation.callBaseMethod(this,"dispose")},
onEnd:function(){
for(var a=0;a<this._animations.length;a++)
this._animations[a].onEnd() },
onStart:function(){
for(var a=0;a<this._animations.length;a++)
this._animations[a].onStart()},
onStep:function(b){
for(var a=0;a<this._animations.length;a++)
this._animations[a].onStep(b)
}
};
Sys.Preview.UI.Effects.CompositeAnimation.descriptor={
properties:[{
name:"animations",type:Array,readOnly:true}]
};
Sys.Preview.UI.Effects.CompositeAnimation.registerClass
("Sys.Preview.UI.Effects.CompositeAnimation",Sys.Preview.UI.Effects.Animation);
请注意,乍一看,CompositeAnimation动画似乎没有提供什么功能,除了一个空空的壳子来容纳子动画!的确如此。但是,这里真正重要的部分正在于这样一个“壳子”—一个封装各种类型子动画的容器!所以,方法get_animations就变得特别重要;我们可以使用它来取得其下各个子动画控件的“句柄”并进而控制每一个子动画。千言莫如一例,还是让我们动手构造一个展示复合动画强大威力的例子吧。
如前面一样,右击工程GlitzTest来添加一个新网页CompositeAnimation2.aspx。下面的图8给出了设计时刻页面CompositeAnimation2.aspx的相应屏幕快照。
图8—复合动画演示网页设计时刻快照。
怎么还是那幅女孩照片!是的。不过,在这个例子中,我们要使这幅照片爆炸开来,直至全部从屏幕上消失。首先,让我们来观察一下页面CompositeAnimation2.aspx相应的HTML编码部分,如下所示:
<div class="h1">CompositeAnimation Demo</div><br/>
<hr/>
<div id='imageArea' class='demosample' style="width: 162px; height: 236px">
<img id="i" src="img/girl1.jpg" onclick="Explode('i');"
style="position: absolute; left: 16px; top: 131px; width: 157px; height: 234px; cursor: hand;" />
</div>
<hr/>
<div id='labelArea' class='demosample1' style="width: 265px; height: 21px">
<label id="Label1" >Click the girl to see what happens!</label>
</div>
实在没有什么特别的内容,对不对?我们首先创建了一个<img>标签,然后是一个使用<div>标签包围的<label>元素……但是,聪明的读者可能已经猜出,其中的秘密一定在那个图像的click事件处理器Explode('i')中。不错,正是这样!下面,让我们来剖析一下相应的编码实现部分:
<script type="text/javascript">在此,我们首先定义了三个全局变量:_scaleFactor指定缩放系数,_image指向<img>元素,_animation存储类Sys.Preview.UI.Effects.CompositeAnimation的实例。接下来,当你点击屏幕中的女孩照片时,将触发click事件处理器,从而调用函数Explode。注意,在此为了避免并发动画发生冲突,我们使用了一个if条件语句来避免创建类CompositeAnimation的多个实例。然后,我们取得图像相应的参数。然后,创建了类NumberAnimation的四个实例来实现同时达到缩放,移动和淡出的效果。最后,创建一个类CompositeAnimation的实例把刚才创建的所有子动画“粘合”到一起,并配置相应的播放动画要求的属性。最后,启动动画。此外,还应注意,我们定义了一个类Sys.Preview.Timer的实例来实现动画播放结束后的某种清除功能,因为文件PreviewScript.js中尽管定义了相应的end事件,却没有相应地实现之(或许因为还在Futures CTP版本中的缘故吧)。好,现在按下F5来领略一下这一消魂的爆炸效果吧!下图9是动画播放过程某一时刻的截图。
//修改此数字可以更改缩放因子
var _scaleFactor = 6;
var _image, _animation;
function Explode(id)
{
//为了避免出现并发动画的冲突
if(_animation != null)
return;
_image = $get(id);
var width = parseFloat(_image.style.width);
var height = parseFloat(_image.style.height);
var x = parseFloat(_image.style.left);
var y = parseFloat(_image.style.top);
//使用一个CompositeAnimation对象实现
//通过缩放、移动和淡出效果“爆炸”目标
var NumberAnimation1 = new Sys.Preview.UI.Effects.NumberAnimation();
NumberAnimation1.set_target(_image);
NumberAnimation1.set_property('style');
NumberAnimation1.set_propertyKey('width');
NumberAnimation1.set_startValue(width);
NumberAnimation1.set_endValue(width * _scaleFactor);
var NumberAnimation2 = new Sys.Preview.UI.Effects.NumberAnimation();
NumberAnimation2.set_target(_image);
NumberAnimation2.set_property('style');
NumberAnimation2.set_propertyKey('height');
NumberAnimation2.set_startValue(height);
NumberAnimation2.set_endValue(height * _scaleFactor);
var NumberAnimation3 = new Sys.Preview.UI.Effects.NumberAnimation();
NumberAnimation3.set_target(_image);
NumberAnimation3.set_property('style');
NumberAnimation3.set_propertyKey('left');
NumberAnimation3.set_startValue(x);
NumberAnimation3.set_endValue(x - (width * (_scaleFactor - 1)) / 2);
var NumberAnimation4 = new Sys.Preview.UI.Effects.NumberAnimation();
NumberAnimation4.set_target(_image);
NumberAnimation4.set_property('style');
NumberAnimation4.set_propertyKey('top');
NumberAnimation4.set_startValue(y);
NumberAnimation4.set_endValue(y - (height * (_scaleFactor - 1)) / 2);
var FadeAnimation1 = new Sys.Preview.UI.Effects.FadeAnimation();
FadeAnimation1.set_target(new Sys.Preview.UI.Image(_image));
FadeAnimation1.set_effect (Sys.Preview.UI.Effects.FadeEffect.FadeOut);
_animation = new Sys.Preview.UI.Effects.CompositeAnimation();
_animation.get_animations().add(NumberAnimation1);
_animation.get_animations().add(NumberAnimation2);
_animation.get_animations().add(NumberAnimation3);
_animation.get_animations().add(NumberAnimation4);
_animation.get_animations().add(FadeAnimation1);
_animation.set_duration(0.3);
_animation.set_fps(35);
_animation.play();
var timer = new Sys.Preview.Timer();
timer.initialize();
timer.set_enabled(true);
timer.set_interval(500);
timer.add_tick(dispose);
}
function dispose(sender)
{
if (!_animation.get_isPlaying())
{
sender.set_enabled(false); //禁用定时器
_image.parentNode.removeChild(_image); //移除图像
_animation = null;
}
}
</script>
图9-复合动画带来的惊人效果。
如何?试想,如果MS AJAX也一并提供了针对二维平台内容(数字及图像)的旋转动画的话,那么,真的是太完美了!但目前这个框架还没有提供这种功能,我坚信勇敢的读者一定会作出尝试的。
【补】ASP.NET AJAX Control Toolkit包中还提供了一种扩展器控件(Extender,这是扩展ASP.NET服务器端控件功能的三大重要技术之一)—AnimationExtender。借助于它,我们可以创建极其丰富的ASP.NET网页动画或开发出自己定制的高级ASP.NET服务器端动画控件。
八、 小结
在本文中,我们全面地探讨了存在于ASP.NET AJAX Futures CTP所带文件PreviewGlitz.js中的所有低级动画控件,并且提供了相应的非常基本的例子。借助于这些动画控件,读者就可以开发出极富人性化的跨平台的基于MS AJAX框架的Web 2.0应用程序。另外,我们还应注意,ASP.NET AJAX Control Toolkit(请
参考http://ajax.asp.net/ajaxtoolkit/Walkthrough/AnimationReference.aspx)也提供了大量的具有更为强大功能的动画控件(事实上,其中的一些就是基于本文中的低级动画API实现),凭借它们,读者可以开发出基于ASP.NET 2.0平台的高级Web应用程序(只是缺少了跨平台的动画支持)。
总之,在浏览器动画方面,还有相当一片天地等待我们去开发,而且本文中的Futures CTP动画也正是在不断发展中的。不过,仅凭现在的动画支持,已经足已使你的Web 2.0应用程序光彩照人了。