(2012-01-08 旧博文搬运)[EssentialActionScript3.0中文版]无责任翻译-23章屏幕更新(1)

[EssentialActionScript3.0中文版]无责任翻译-23章屏幕更新(1)

  • 空间

Chapter 23. Screen Updates

从概念上来说,AS中所有的屏幕更细都可以被分为2类:每隔一段时间发生(预制更新),以及在事件侦听器中执行的立即更新(事件后更新)。不管这些种类,所有的屏幕更新都是自动的。在AS中,没有方法可以任意让一个屏幕更新立刻发生。作为替代,由创作工具或程序创建的可视内容会自动由预制更新或是事件后更新渲染。这一章将研究者2种屏幕更新。

 

鉴于本书注重纯代码而不是.swf创作工具,下面的讨论需要一些基本的flash创作工具时间线及时间线脚本的知识。如果你不熟悉创作工具,可以先看Chapter 29.

 

23.1. 预制屏幕更新

在AS中,屏幕更新与FLASH运行时的动画能力有关系。甚至用Flex或mxmlc控制线编译器创建的纯AS代码程序也受到FLASH运行时的以动画为中心的屏幕更新系统的支配。FLASH运行时的屏幕更新系统被设计用于适应flash创作工具的模型以便创建脚本设计的动画内容。在flash创作工具中,动画内容以时间轴中的若干帧的形式被手动地产生,这些针就像一个幻灯片的帧。每一个可视帧都可以与一段代码联系起来,即帧代码。术语中,当flash运行时播放一段由创作工具绘制的动画,它沿着下面的屏幕更新循环:

1. 执行当前帧代码.

2. 更新屏幕.

3. Go to 下一帧.

4. 重复

 

比如,假设我们有一个3帧长度的动画,它由创作工具创建,并且每一帧都有帧代码。则flash运行时按照如下顺序播放动画:

1. 执行第1帧的帧代码.

2. 显示第1帧的内容.

3. 执行第2帧的帧代码.

4. 显示第2帧的内容.

5. 执行第3帧的帧代码.

6. 显示第3帧的内容

 

在第1、3和5步,每一个帧代码都可创建一个新的可视内容或是修改一个已经存在的可视内容。因此,对这段动画回放的步骤更精确的描述是:

1. 执行第1帧的帧代码.

2. 显示第1帧的内容并且渲染第一帧代码的可视输出.

3. 执行第2帧的帧代码.

4. 显示第2帧的内容并且渲染第一帧代码的可视输出.

5. 执行第3帧的帧代码.

6. 显示第3帧的内容并且渲染第一帧代码的可视输出.

 

在上面的顺序表,注意在渲染一个给定的帧代码的可视输出之前,flash已经运行完了这整段代码。

 

NOTE

Flash运行时绝不会为了更新屏幕而打断一个帧代码的执行。前面6步执行的速度是被flash运行时的帧频决定的,帧频是每秒执行的帧数。比如,假设一个动画被设置为一个每秒1帧这样非常慢的速度,并且假设每一帧的代码都正好花费100毫秒来执行,每一帧的内容都需要花费50毫秒来渲染。关于播放这段动画,理论上上面的6步将沿着如下的顺序:

0ms:第1帧的代码开始执行

100ms: 第1帧的代码执行完毕

1000ms: 第1帧的内容和脚本的输出开始渲染

1050ms: 第1帧的内容和脚本的输出渲染完毕

1051ms: 第2帧的代码开始执行

1151ms: 第2帧的代码执行完毕

2000ms: 第2帧的内容和脚本的输出开始渲染

2050ms: 第2帧的内容和脚本的输出渲染完毕

2051ms: 第3帧的代码开始执行

2151ms: 第3帧的代码执行完毕

3000ms: 第3帧的内容和脚本的输出开始渲染

3050ms: 第3帧的内容和脚本的输出渲染完毕

 

主要到,在每一帧的脚本执行结束之后,flash运行时不会立刻更新屏幕。而是在下一个预制帧渲染时间才渲染这一帧的脚本输出。Flash运行时屏幕更新因此可以被看做预制的屏幕更新,因为他们是根据预先定制好的帧频来发生的。因此,一个动画回放的更精确的步骤为:

1.执行第1帧代码.

2.等待直到下一个帧渲染时

3.显示第1帧的内容并渲染第1帧代码的可视输出

4.执行第2帧代码.

5.等待直到下一个帧渲染时

6.显示第2帧的内容并渲染第2帧代码的可视输出

7. 执行第3帧代码.

8.等待直到下一个帧渲染时

9.显示第3帧的内容并渲染第3帧代码的可视输出

 

 

现在我们假设第1帧的帧代码注册了一个事件侦听器函数,clickListener(), 注册在Stage 实例上,为了侦听MouseEvent.CLICK事件. 每当 clickListener()运行时,它会绘制一个红色线到鼠标当前的位置。下面是第一帧的代码:

import flash.events.*;

import flash.display.*;

stage.addEventListener(MouseEvent.CLICK, clickListener);

function clickListener (e:MouseEvent):void {

graphics.lineStyle(2, 0xFF0000);

graphics.lineTo(e.stageX, e.stageY);

}

 

在第1帧代码执行结束后立马,clickListener()就具备了侦听MouseEvent.CLICK 事件的资格.

 

现在假设用户在动画开始后的500毫秒时点击了flash运行的可视区域(在前面介绍的顺序列表中的第2步的等待阶段)。clickListener() 方法会立马执行,但是在下一个帧渲染时到来之前他不会显示在舞台上。在下一个帧渲染时,clickListener() 的可视输出才会连同第1帧的内容以及第一帧代码的可视输入一起被渲染。因此,一个动画回放的更精确的步骤为:

1. 执行第1帧的代码.

2. 等待下一个预定帧渲染时的到来。在等待过程中,如果任何事件被触发,执行与之关联的侦听器

3. 显示第1帧的内容;渲染第1帧代码的可视输出;渲染第2步中任何事件侦听器的可视输出

4. 执行第2帧的代码.

5. 等待下一个预定帧渲染时的到来。在等待过程中,如果任何事件被触发,执行与之关联的侦听器

6. 显示第2帧的内容;渲染第2帧代码的可视输出;渲染第5步中任何事件侦听器的可视输出

7. 执行第3帧的代码.

8. 等待下一个预定帧渲染时的到来。在等待过程中,如果任何事件被触发,执行与之关联的侦听器

9. 显示第3帧的内容;渲染第3帧代码的可视输出;渲染第8步中任何事件侦听器的可视输出

 

NOTE

前面的步骤反映了flash运行时的默认屏幕更新行为。但是,对于某一事件类型,flash运行时可以被强制地立刻更新屏幕。详细请看"事件后屏幕更新,"在本章的后面将讲述。

 

现在假设第2帧的内容与第1帧相同,并且第2帧的帧代码不形成任何可视输出,在从第1帧到第2帧过程中也没有事件侦听器被调用。这种情况下,flash不会重新渲染显示区域。作为替代,当第2帧的帧渲染时刻到来时,flash仅仅检查屏幕是否需要更新。第2帧没有可视的变化,所以屏幕不会重新渲染。

 

因此,对上述步骤的更详细的描述应该为:

1. 执行第1帧的代码.

2. 等待下一个预定帧渲染时刻到来。等待的过程中如果发生了任何事件,执行侦听器。

3. 在帧渲染时刻,检查屏幕是否需要更新。如果下面任何一条符合,就进行更新:

• 第1帧中有通过flash创作工具包含了对舞台内容的更改.

• 第1帧中的代码创建了新的可视内容或改变了已经存在的可是内容.

• 在第2步中执行的侦听器函数的代码创建或修改了可视内容.

4. 如果必要,更新屏幕以体现第3步中的变化.

5. 为第2帧和第3帧重复第1-4步.

 

为了本章的后文以及后面章节描述方便,我们称第3步中发生的屏幕更新检查为预定屏幕更新检查.每当flash运行时执行一个预定屏幕更新检查时,它都会发送一个Event.ENTER_FRAME事件 (哪怕屏幕事实上不需要更新). 作为响应这个Event.ENTER_FRAME事件,对象们可以在屏幕更新的时候循环执行某个任务。在Chapter24, 我们将学习如何使用Event.ENTER_FRAME 事件,用纯代码来创建动画内容.

 

Ready for one last hypothetical scenario? 假设我们从动画里移除第2帧和第3帧,只剩1帧。那么如之前一样,第1帧的脚本定义了一个MouseEvent.CLICK 事件侦听器,clickListener()。一旦第1帧的内容和帧脚本输出被渲染(在上面步骤列表中的第4步),则动画就已经结束了播放。然而,为了可以持久地进行事件处理,flash运行时的屏幕更新循环必须仍处于活动状态。因此,对于一个仅包含了1帧的.swf文件,屏幕更新循环是如下(下面的步骤同样适用于在第1帧就被暂停的多帧swf文件):

 

1. 执行第1帧的代码.

2. 等待下一个预定帧渲染时刻到来。等待的过程中如果发生了任何事件,执行侦听器。

3. 在帧渲染时刻,检查屏幕是否需要更新。如果下面任何一条符合,就进行更新:

• 第1帧中有通过flash创作工具包含了对舞台内容的更改.

• 第1帧中的代码创建了新的可视内容或改变了已经存在的可是内容.

• 在第2步中执行的侦听器函数的代码创建或修改了可视内容.

4. 如果必要,更新屏幕以体现第3步中的变化.

5. 等待下一个帧渲染时刻。等待的过程中如果任何事件发生,执行侦听器。

6. 在帧渲染时刻,检查屏幕是否需要更新。如果在第5步中执行的事件侦听器的代码创建或修改了可视内容,则需要更新。

7. 如果必要,更新屏幕以体现第6步中的变化.

8. 重复5-7步.

 

上面列表中的第5-8步无限期地在swf运行期间重复,因此绑定了所有后续的代码到基于帧频的屏幕更新循环。在Chapter 20, 我们学习了"当一个空白的flash运行时打开一个新的swf文件,它定位这个swf 文件的主类,创建主类的实例,然后添加这个实例到显示列表作为Stage实例的第一个child." 对于纯AS程序,在主类实例的构造方法完成后,屏幕立马更新。所有后续的屏幕更新的发生都于上述基于帧频的屏幕更新循环的第5-8步一致。比如,假设下面这个非常简单的绘画程序,为了强调屏幕更新而设置帧频为1帧/秒。

package {

import flash.display.*;

import flash.events.*;

public class SimpleScribble extends Sprite {

public function SimpleScribble () {

stage.frameRate = 1;

graphics.moveTo(stage.mouseX, stage.mouseY);

stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveListener);

}

private function mouseMoveListener (e:MouseEvent):void {

graphics.lineStyle(2, 0xFF0000);

graphics.lineTo(e.stageX, e.stageY);

}

}

}

SimpleSribble 的构造函数创建了一个无图像的内容,但是注册了一个侦听器mouseMoveListener(), 来侦听MouseEvent.MOUSE_MOVE 事件. 每当鼠标移动, mouseMoveListener() 画一条线到鼠标当前的位置。但是,这条线在下一个预定屏幕更新到来之前不会显示在屏幕上,更新的到来会一秒一次。因此,每隔一秒,flash运行时都会用一系列显示鼠标路径的线条来更新屏幕。要更加顺滑,可以将帧频提高到30,或者可以使用"Post-Event Screen Updates."技术来强制更新屏幕。

 

让我们来看看目前为止的重点:

 

• ActionScript的屏幕更新系统是全自动的

• 对于纯ActionScript 应用程序,帧频可以被看做是flash运行时每秒检查屏幕是否需要更新的次数。比如,如果flash运行时帧频为1,那么被程序进行的可视变化就会在一秒后自动被渲染;如果帧频为10,那么可视变化就会在1秒内被渲染10次(100ms一次)。

• 如果帧频非常低(比如1-10帧/秒),那么在代码执行到代码的可视输出被渲染就会存在明显的延迟。

• 每当flash运行时进行一个预定的屏幕更新检查,他就会发送一个Event.ENTER_FRAME 事件.

• FlashPlayer绝不会为了更新屏幕而打断代码块的执行。

 

最后一条重点非常重,所以让我们在详细调查一下。

 

 

23.1.1. 代码块中不进行屏幕更新

如我们在前面节学习的, Flash 运行时不会为了更新屏幕而打断代码块。在一个预定屏幕更新发生之前,所有调用堆栈中的函数以及当前帧的所有代码都必须被执行完毕。同样,在一个事件后屏幕更新发生之前,调用updateAfterEvent()的侦听器函数必须执行完毕。事实上,即使当一个屏幕更新被预先设置为在一个给定的时间发生,这个更新也必须延迟到到正在执行的代码之后发生。屏幕更新和代码执行是FLASH运行时单独分开进行的任务;他们常常连续地发生,而不是同时发生。一般来说,要记住在ActionScirpt中,屏幕不会再2行代码中间被更新。比如,在下面的函数displayMsg(), 创建了一个TextField 并且设置了2次它的水平位置;第一次50,第二次100。

 

public function displayMsg():void {

var t:TextField = newTextField();

t.text = "Are wehaving fun yet?";

t.autoSize =TextFieldAutoSize.LEFT;

addChild(t);

t.x = 50;

t.x = 100;

}

displayMsg() 执行时,屏幕将不会也无法再最后2行的时候更新2次屏幕。结果,TextField绝不会在屏幕上显示它的位置到 50。这个函数会在屏幕渲染之前全部执行完毕并显示TextField到100的位置。即使它的x属性曾经短暂地改变为50,而屏幕上绝对不会呈现出这种改变的视觉效果。

 

一些情况下,代码的执行可以延迟屏幕更新数秒,这取决于编译器的max-execution-time属性,默认是设为15 秒。任何代码如果没有在max-execution-time 设定的时间内完成,就会生成一个

ScriptTimeoutError 异常。处理ScriptTimeoutError 异常,请看Adobe AS语言参考中的flash.errors.ScriptTimeoutError。

 

要避免ScriptTimeoutError 异常,所有的代码必须设计成可以在编译器的max-execution-time属性中设置的时限内执行完毕。要执行一个超过时限的任务,可以把它分为多个片段执行,并使用一个

Timer 来控制执行这些片段。

 

23.1.2. 设置帧频

Flash Player的帧频可以通过3种方法来设置:

• 使用mxmlc 编译器参数default-frame-rate

• 在flash创作工具中使用文档属性对话框

• 在一个运行的swf文件中使用Stage类实例的frameRate属性

 

第一个被flash运行时加载进来的swf文件的初始帧频决定了后续加载进来的swf文件的帧频。

 

无论帧频被如何设置,它都被后续加载进来的swf文件所遵循(覆盖他们本身的帧频)。但是,一旦第一个swf文件被加载,flash运行时的特定的帧频可以通过Stage类的实例属性frameRate被重新指定,frameRate属性接受的范围从0.01 (每100秒一帧) 到1000 (每一秒1000帧).比如,下面的类设置了帧频到150/秒:

package {

import flash.display.*;

public class SetFrameRate extends Sprite {

public function SetFrameRate () {

stage.frameRate = 150;

}

}

}

 

鉴于帧频可以被任何对象通过访问Stage 实例而设置,显示对象们无法在不同的帧频中运行。显示列表中所有的对象都会在同一时间被渲染,取决于flash运行时的单帧频。

 

NOTE

在ActionScript 3.0之前,flash运行时的帧频无法通过程序化的方式改变。

 


收藏于 2012-01-08

你可能感兴趣的:((2012-01-08 旧博文搬运)[EssentialActionScript3.0中文版]无责任翻译-23章屏幕更新(1))