321
第十三章
影片剪辑
每一个 Flash文档都包含一个场景(放置形状、文本和其他视觉元素的地方)以及
一个主时间线,在时间线上定义场景内容随时间所发生的变化。场景(也就是主影
片)可以包含单独的子影片,命名的影片剪辑(简称剪辑)。每一个影片剪辑有它自
己独立的时间线和画布(场景就是主影片的画布),甚至能够包含其他影片剪辑。一
个包含其他剪辑的剪辑被称为所包含剪辑的主剪辑或者父剪辑。
一个单独的 Flash文档可以包含一个相关影片剪辑的层次。例如,主影片可以包含
一个庞大的场景。一个包含动态人物的独立影片剪辑可以在场景上移动,表现人物
的行走动作。人物剪辑里的另外一个影片剪辑可以独立地完成人物中的眨眼。当卡
通人物中的独立元素一起播放的时候,它们会显得像是一个整体的内容。此外,每
一个成分可以快速地响应其他成分 —— 我们可以在人物停止移动的时候让眼睛开
始眨动,或者当人物开始移动的时候让腿开始行走。
ActionScript提供了影片剪辑的控制细节:我们可以播放一个剪辑或停止,在它的
时间线内移动播放头,在程序中计划性地设置它的属性(比如尺寸、旋转、透明程
度,以及在场景中的位置)以及将它作为真实的编程对象来操作。作为ActionScript
语言的常见成分,影片剪辑可以被看成用来产生Flash的程序化生成内容的原料。例
如,一个影片剪辑可以作为小游戏中的一个球或者一片桨,作为购物网站中的一个
定货单,或者作为动画中背景声音的容器。在本章的最后,我们会将影片剪辑用作
闹钟上的指针以及多项选择测试中的答案。
第十三章322
影片剪辑的对象性
在 Flash 5中,影片剪辑可以像我们在第十二章中学到的那样,作为对象来进行操
作。我们可以获取和设置剪辑的属性,也可以在剪辑上调用内置的或者自定义的方
法。和其他对象不同,剪辑上所执行的操作可以在播放器中产生可见的或者可听的
结果。
影片剪辑并不真是一种对象,它既没有MovieClip类构造器,也不能在代码中用一
个对象常量来实例化一个影片剪辑。既然影片剪辑不是对象,那么它又是什么呢?
它们属于一种类似对象的数据类型,称为 movieclip(我们可以在影片剪辑上执行
typeof来证明这一点,它会返回串“movieclip”)。影片剪辑和真正的对象之间的主
要区别是它们的分配(创建)方式以及解除分配(除去,或者说释放)的方式。要
了解详细的内容,请参见第十五章。但是,不管这个技术如何,我们几乎总是将影
片剪辑当做对象来看待。
那么,影片剪辑的对象性会对我们在ActionScript中使用影片剪辑时产生什么样的
影响呢?最值得注意的是,它表示我们控制剪辑以及检查其属性的方法。影片剪辑
可以通过内置方法进行直接的控制。例如:
eyes.play();
可以用点操作符来获取和设置一个影片剪辑的属性,正如可以访问任何对象的属性
一样:
ball._xscale = 90;
var radius = ball._width / 2;
影片剪辑中的一个变量只是剪辑的一个属性,可以使用点操作符来设置和获取变量
的值:
myClip.myVariable = 14;
x = myClip.myVariable;
子影片剪辑可以作为其父影片剪辑的对象属性来对待,因此,可使用点操作符来访
问嵌套的剪辑:
clipA.clipB.clipC.play();
323影片剪辑
可使用保留的 _parent属性来指向包含当前剪辑的剪辑:
_parent.clipC.play();
将剪辑作为对象为我们提供了无数简便语法,以及对播放的方便控制。但是,将剪
辑作为对象也让我们把剪辑作为数据来管理。我们可以将一个影片剪辑存储在数组
元素,或者变量中,甚至将剪辑的引用作为参数传递给函数!例如,下面的代码是
一个函数,它将一个剪辑移动到屏幕上指定的位置:
function moveClip (clip, x, y) {
clip._x = x;
clip._y = y;
}
moveClip(ball, 14, 399);
在本章剩余的部分中,我们要学习将剪辑作为数据对象进行引用、控制以及操作的
规则。
影片剪辑的类型
并非所有的影片剪辑的创建都是相同的。实际上,在 Flash中有三种类型的剪辑:
● 主影片。
● 正规影片剪辑。
● 智能剪辑。
除了这三种正式的类型之外,还可以基于对正规影片剪辑的使用将其分为四类:
● 过程剪辑。
● 脚本剪辑。
● 链接剪辑。
● 种子剪辑。
后面这些非正式的分类并不是 ActionScript中使用的正规形式,它们只是提供了一
种方便的途径来处理影片剪辑的编程。下面,我们来看看每一种影片剪辑类型。
第十三章324
主影片
Flash文档的主影片包含基础时间线和出现在每个文档中的场景。主时间线是文档所
有内容的基础,包括所有其他影片剪辑。我们有的时候将主影片称为主时间线、主
影片时间线、主场景,或者简单叫做 root。
主影片的操作方法和正规影片剪辑非常类似,但是:
● 一个主影片不能从 .swf文件中被删除(虽然一个 .swf文件本身可以从 Flash播
放器中删除)。
● 下面的影片剪辑方法在主影片上调用的时候无效:dupl ica teMovieCl ip( ),
removeMovieClip(),swapDepths()。
● 事件处理器不能被添加到主影片上。
● 主影片可以通过内置的全局属性 _root和 _leveln来引用。
注意,每一个 .swf文件只包含一个主影片,而 Flash播放器中一次可以包含多个 .swf
文件,我们可以将多个 .swf文档(因此有多个主影片)通过 loadMovie()和 unload-
Movie()函数装载到 levels的栈中,我们将在后面学习这个内容。
正规影片剪辑
正规影片剪辑是最常用、最基础的内容容器,它们包括视觉和听觉元素,还可以通
过事件处理器对用户输入和影片的播放起反应。对熟悉DHTML的 JavaScript编程
人员来说,将主影片看成是和HTML文档对象类似的东西,将正规影片剪辑看成是
文档的层对象会有所帮助。
智能剪辑
在 Flash 5中推出的智能剪辑是一个包含用来在创建工具中自定义剪辑属性的图形
用户界面的正规影片剪辑。智能剪辑一般由高级程序员来开发,它为缺少经验的
Flash创作者提供一个在不知道剪辑代码是如何工作的情况下自定义影片剪辑行为的
简便方法。我们将在第十六章讨论智能剪辑的细节内容。
325影片剪辑
过程剪辑
过程剪辑并不用来包容影片内容,只是用来重复地执行一个代码块。过程剪辑可以
用 enterFrame事件处理器或者我们在第八章中学习的时间线循环来建立。
过程剪辑是 ActionScript中对 JavaScript窗口对象的 setTimeout()和 setInterval()方
法的非正式选择。
脚本剪辑
和过程剪辑一样,脚本剪辑不用来包含内容,而用来跟踪一些变量或者执行某些脚
本的空影片剪辑。例如,可以使用一个脚本剪辑来包含对击键和鼠标事件起反应的
事件处理器。
链接剪辑
链接剪辑是一种从影片库中既可以导入又可以导出的影片剪辑。导出和导入设置在
每一个影片库中的剪辑链接选项中是可以选择的。在使用attachMovie()剪辑方法直
接从符号库中动态生成一个剪辑实例的时候,我们经常使用链接剪辑,这方面的内
容我们马上就会看到。
种子剪辑
在 attachMovie()方法被引入到 Flash 5中之前,我们使用 duplicateMovieClip()函
数,基于某些现存的剪辑(称为种子剪辑),来创建新的影片剪辑。种子剪辑是场景
中只通过 duplicateMovieClip()来进行复制的影片剪辑。随着attachMovie()的引入,
就不再需要种子剪辑了。但是,在希望保留一个剪辑的事件处理器和复制过程中的
转换的时候,仍然使用种子剪辑和 duplicateMovieClip()。
在一个大量使用duplicateMovieClip()来动态生成内容的影片中,经常可以在影片画
布的外缘看到很多种子剪辑。种子剪辑只用来复制剪辑,因此不出现在场景中。
第十三章326
创建影片剪辑
我们经常将影片剪辑当成数据对象 —— 用点操作符来设置它们的属性;用函数调
用操作符(括号)来调用它们的方法;将它们存储在变量、数组元素和对象属性中。
但是我们不能用创建对象的方法来创建影片剪辑。我们不能在代码中像描述一个对
象一样来直接描述一个影片剪辑。不能用下面的影片剪辑构造器函数来生成影片剪
辑:
myClip = new MovieClip(); // 虽然是很好的尝试,但它是无效的
我们只能在创建工具中亲手直接创建影片剪辑。一旦剪辑得到创建,就可以用诸如
duplicateMovieClip()和 attachMovie()这样的命令来为它建立新的、独立的复本。
影片剪辑符号和实例
正如所有的对象实例都基于一个或者其他的类一样,所有的影片剪辑实例都是基于
模板影片剪辑的,也就是符号(symbol)(有的时候称为定义)。影片剪辑符号用来
作为剪辑内容和结构的模型。我们在生成一个特定的剪辑对象之前,总是必须有一
个影片剪辑符号。使用符号,我们可以用手动和程序化两种方式来创建在影片中渲
染的剪辑。
在场景中渲染的影片剪辑称为一个实例。实例是单个的剪辑对象,可以用
ActionScript来操作,符号是所有特定影片剪辑实例起源的模板。影片剪辑符号在
Flash创建工具中创建。要建立一个新的、空白的剪辑,应执行下面的步骤:
1. 选择 Insert→ New Symbol(新符号),就会出现 Symbol Properties(符号属
性)对话框。
2. 在 Name(名称域)中,输入符号的标识符。
3. 选择 Movie Clip(影片剪辑)单选钮。
4. 点击 OK。
通常,下面要执行的步骤就是用影片剪辑的内容来填充符号的画布和时间线。一旦
一个符号得到创建,它就存在于库中,等着我们用它来制作一个实际的影片剪辑实
327影片剪辑
例。但是,也可以将一批已经存在于场景中的形状和对象转换为影片剪辑符号。要
这么做,我们执行下面的步骤:
1. 选择要进行转换的形状和对象。
2. 选择 Insert→ Convert to Symbol(转换为符号)。
3. 在 Name中,输入符号的标识符。
4. 选择Movie Clip单选钮。
5. 点击 OK。
我们用来创建新影片剪辑符号的形状和对象可以被新剪辑的未命名实例所代替。对
应的影片剪辑符号会出现在库中,准备用来创建其他实例。
创建实例
要基于影片剪辑符号来创建新的实例,有三种方法。其中两种是程序化的,另外一
种是手动的,要在 Flash的创建工具中完成。
手动创建实例
可以使用 Flash制作环境中的库来手动地创建影片剪辑实例。物理地将影片剪辑符
号从库中拖到场景上,这样就生成了一个新的实例。一个这样创建的实例要通过实
例面板手动地进行命名。(我们会在后面学习关于实例名称的更多内容。)如果你从
来没有在 Flash中接触过影片剪辑,请参考Macromedia Flash 帮助中的 Using
Symbols and Instances(使用符号和实例)。
用 duplicateMovieClip()来创建实例
任何已经存在于 Flash影片场景中的实例都可以用ActionScript来复制。然后,我们
可以将单独的复本当作一个完全独立的剪辑。不管是程序创建的还是手动创建的实
例都可以被复制。换句话说,复制一个复本是合法的。
实际上,用 duplicateMovieClip()来复制实例有两种方法:
● 可以将 duplicateMovieClip()作为一个全局函数来调用,使用下面的语法:
第十三章328
duplicateMovieClip(target, newName, depth);
target是我们想复制的实例名称的串;newName参数是一个串,它为新实例指
定标识符;depth是一个整数,指定我们要将新的实例放在程序化生成剪辑栈
中的什么地方。
● 还可以将 duplicateMovieClip()作为一个现有剪辑的方法来调用:
myClip.duplicateMovieClip(newName, depth);
myClip是我们要复制的剪辑的名称,newName和 depth都和上面的方法一
致。
通过duplicateMovieClip()进行复制的时候,一个实例最初可以直接放到它的种子剪
辑的顶层。因此,复制后的第一个任务,通常是将复制的剪辑移动到一个新的位置。
例如:
ball.duplicateMovieClip("ball2", 0);
ball2._x += 100;
ball2._y += 50;
其种子剪辑被ActionScript或者Flash中的制作工具变换(例如,上色、旋转或者重
新设置大小)的复制实例继承种子剪辑的初始变换。种子剪辑后来的变换不影响复
制实例。同样,每一个实例可以被单独转变。例如,如果一个种子剪辑旋转了45度,
然后复制一个实例,那么实例的初始旋转度就是 45度:
seed._rotation = 45;
seed.duplicateMovieClip("newClip", 0);
trace(newClip._rotation); // 显示:45
如果我们继续将实例旋转 10度,它的旋转角就是 55度,但是种子剪辑的旋转角仍
然是 45度:
newClip._rotation += 10;
trace(newClip._rotation); // 显示:55
trace(seed._rotation); // 显示:45
连续复制许多实例,并稍微调整每一个复本的转变,我们就可以得到有趣的复合转
变(这个技术在例 10-2的 load事件中有所表现)。
使用 duplicateMovieClip()通过ActionScript来复制剪辑,与手动地将剪辑放到影片
中相比有以下一些优点:
329影片剪辑
● 当剪辑出现在和程序执行有关的场景中时可以精确控制。
● 当剪辑从和程序执行有关的场景中被删除时可以精确控制。
● 赋给一个和其他复制剪辑相关的复制剪辑的层深度(这一点和 Flash 4有很大
关系,Flash 4不允许影片的层栈被改变。)
● 复制一个剪辑的事件处理器。
这些能力可以提高对影片内容的程序化控制。太空船游戏就是一个显著的例子。当
太空船的火力按钮被按下的时候,需要复制一个飞弹影片剪辑,这个飞弹剪辑可以
程序化地移动,然后放在影片中障碍物的后面,最后,在和敌舰相撞后被删除。手
动剪辑就没有这种灵活性。用手动创建的剪辑,必须用时间线来预先指定该剪辑的
产生和死亡,在 Flash 4中,还不能改变剪辑的层。
用 attachMovie()创建实例
和 duplicateMovieClip()一样,attachMovie()方法可创建一个影片剪辑实例,但是,
和 duplicateMovieClip()不同,它不要求预先创建实例 —— 它直接在影片库中从符
号创建实例。为了使用attachMovie()来创建一个符号的实例,必须首先从库中导出
这个符号。请看下面的步骤:
1. 在库中选择所要的符号。
2. 在Library's Options(库选项)菜单中,选择Linkage(链接),就会出现Symbol
Linkage Properties(符号的联接属性)对话框。
3. 选择 Export This Symbol(导出该符号)单选钮。
4. 在 Identifier(标识符)域中,为剪辑符号输入一个独特的名称。名称必须是串
(通常都和符号本身同名)但是应该和所有其他导出的剪辑符号不同。
5. 点击 OK。
一旦导出了剪辑符号,就可以将该符号的新实例添加到已经存在的剪辑中,用下面
的语法调用 attachMovie()就可以了:
myClip.attachMovie(symbolIdentifier, newName, depth);
第十三章330
myClip是要添加新实例的剪辑的名称。如果 myClip被遗漏,attachMovie()就会将
新的实例添加到当前剪辑(attachMovie()语句所在的剪辑)。symbolIdentifier参
数是一个串,它包含要用来产生实例的符号,和库中链接选项的标识符域所指定的
一样;newName也是串,它指定我们创建的新实例的标识符;depth是个整数,它
指定要把新实例放到主剪辑分层栈的什么地方。
当将实例添加到另外的剪辑时,实例就被放在剪辑的中央,剪辑分层栈之间(我们
马上就要讨论剪辑栈)。当将实例添加到文档主影片上的时候,实例被放到场景的左
上角,坐标为(0,0)。
实例名称
我们创建实例的时候,给它们赋了标识符,或者说是实例的名称,这可以让我们以
后引用它们。注意这和正规对象的不同。当我们创建一个平常的数据对象(不是影
片剪辑)的时候,必须将这个对象赋给一个变量或者其他数据容器,以便让该对象
继续存在,我们也可以在以后用名称来引用它。例如:
new Object(); // 对象在创建之后立即死亡,不能引用它
var thing = new Object(); // 对象引用存储在 thing中,以后可以作为 thing引用
影片剪辑实例不需要被存储在变量中以便引用。和普通的数据对象不同,剪辑实例
在 ActionScript中可以通过它们的实例名称在创建之后检索。例如:
ball._y = 200;
每一个剪辑实例名称都存储在它的内置属性_name中,它可以被获取,也可以被设
置:
ball._name = "circle"; // 将 ball的名称改为circle
当改变实例的_name属性时,以后指向该实例的全部引用都必须使用这个新的名称。
例如,在前边的代码执行之后,ball引用就不存在了,我们要使用circle来检索
这个实例。
实例最初得到它的实例名称的方式取决于它是如何创建的。程序化产生的实例用创
建它的函数来命名。手动创建的实例通常在制作工具中通过实例面板赋给显式的实
例名称,如下所示:
331影片剪辑
1. 选择场景上的一个实例。
2. 选择 Modify(修改)→ Instance(实例)。
3. 将实例名称输入到 Name域中。
如果手动创建的剪辑没有给予实例名称,它会在运行的时候由 Flash播放器自动赋
给一个。自动产生像 instance1,instance2,instance3...instancen这样的实
例名称,但是,这些名称对剪辑的内容没有什么有意义的描述(我们必须猜想自动
产生的这些名称究竟是指什么)。
警告:因为实例名称是标识符,必须按照合法标识符的创建规则对它们进行调整,如第十四章
中所描述的那样。更为特别的是,实例名称不应该以数字开头,也不应该包括连字符号
和空格。
导入外部的影片
我们已经讨论了在单个文档中创建影片剪辑实例的内容,但是,Flash播放器也可以
同时显示多个 .swf文档。我们可以用 loadMovie()(作为全局函数或者影片剪辑方
法)将一个外部的 .swf文件导入到播放器中,并把它放在剪辑实例中或者编号层次
中的基础影片之上(也就是和基础影片有关的前景中)。通过对这些独立文件内容的
管理,即可对下载过程有精确的控制。例如,假设有一个影片,它包含一个主导航
菜单和五个子片段。在用户进入第五个片段之前,片段 1到片段 4必须已经完成下
载。但是,如果我们将每个片段放在单独的 .swf文件中,片段就可以以任意的顺序
来装载,可以让用户直接访问每一个片段。
当外在的 .swf文件被装载到一个层中的时候,它的主影片时间线就成为了该层的根
时间线,它将代替在前面载入到这个层中的任何影片。当外部影片装载到剪辑中的
时候也是如此,载入影片的主时间线将代替剪辑的时间线,将该剪辑现有的图形、
声音和脚本卸载掉。
和 duplicateMovieClip()一样,loadMovie()可以用来作为一个独立的函数,也可以
作为实例的方法。loadMovie()的独立语法如下:
loadMovie(URL, location)
第十三章332
URL指定要装载的外在 .swf文件地址。location参数是一个串,它表示到现有剪
辑的路径,或者是要容纳新的 .swf文件的文档层(也就是说装载的影片要放在什么
地方)。例如:
loadMovie("circle.swf", "_level1");
loadMovie("photos.swf", "viewClip");
因为一个影片剪辑引用在作为串使用的时候转换成了路径,location也可以作为影
片剪辑的引用来提供,比如用_level1来代替"_level1"。但是使用引用的时候
要小心。如果提供的引用并没有指向一个合法的剪辑,loadMovie()函数就不会产生
期望的结果 —— 它将外部 .swf文件装载到当前的时间线。参见第三部分可以得到
更多的细节内容,或者参见本章后面的内容。
loadMovie()的剪辑方法版本的语法如下:
myClip.loadMovie(URL);
作为方法来使用的时候,loadMovie()假设我们正在将外部 .swf文件装载到myClip
中,因此,不需要独立的 loadMovie()函数所要求的 location参数,所以,我们只
通过 URL参数提供对 .swf的装载路径。自然,URL可以是一个局部文件名,比如:
viewClip.loadMovie("photos.swf");
被放置到一个实例中的时候,装载影片采用该剪辑的属性(比如剪辑的缩放、旋转、
颜色变换等等)。
注意,myClip必须存在,以便 loadMovie()能以它的方法形式来使用。例如,下面
的代码中,如果 _level1为空,circle.swf的装载就会失败:
_level1.loadMovie("circle.swf");
装载影片的执行顺序
loadMovie()函数出现在一个语句块中的时候并不会立刻执行。实际上,它要等到块
中所有其他语句都执行完了以后才执行。
注意:调用 loadMovie()来将外部影片装载到播放器中的时候,我们不能在同一个语句块中访
问这个外在影片的属性或者方法。
333影片剪辑
当 attachMovie()和 loadMovie()一起使用的时候
将一个外部 .swf文件用 loadMovie()装载到一个剪辑实例中会产生惊人的结果
—— 不可以用 attachMovie()对这个剪辑添加实例。只要某个剪辑里装载了一
个外部的 .swf文件,就不能再从这个剪辑所起源的库中添加任何影片到这个剪
辑。例如,如果movie1.swf包含了一个名为clipA的实例,我们将movie2.swf
装载到 clipA中,我们就不能再从 movie1.swf库中向 clipA添加实例了。
为什么? attachMovie()方法只在单个的文档中有效。也就是说,不能从一个
文档的库中添加剪辑到其他的文档。当我们为剪辑装载了一个 .swf文件时,我
们就为这个剪辑组装了一个新的文档,也就是一个新的(不同的)库。以后还
想从原来的文档中添加实例到这个剪辑就会失败,因为剪辑的库不再和原来的
文档库相匹配了。但是,如果我们通过 unloadMovie()卸载剪辑里的文档,就
又可以从它自己的文档库中添加影片到这个剪辑了。
类似的,将一个 .swf文件用 loadMovie()装载到剪辑中就不能再通过 duplicate-
MovieClip()来复制这个剪辑。
因为 loadMovie()装载了一个外部文件(通常是通过网络),它的执行就是异步的。
也就是说,loadMovie()可以在任何时间结束,这取决于文件转换的速度。因此,在
访问一个装载影片之前,我们总是要检查影片是否全部转移到了播放器。我们通常
用所谓的预装载器来完成这个工作 —— 这是一种简单的代码,它在允许某些动作
发生之前检查文件装载了多少。预装载可以用 _totalframes和_framesloaded影
片剪辑属性,以及 getBytesLoaded()和 getBytesTotal()影片剪辑方法来建造。参见
第三部分示例代码中对应的条目,也可以参见例 10-4,它给出了如何使用data剪辑
事件来构造预装载器的方法。
影片和实例的堆栈顺序
所有的影片剪辑实例和显示在播放器中的外部载入影片都按照一个可视的堆栈顺序
存放,就跟一副纸牌类似。当实例或者外部载入 .swf文件在播放器中产生重叠的时
候,一个剪辑(两者中的“高”者)总是掩盖了其他剪辑(两者中的“低”者)。原
则上很简单,但是主堆栈包含了所有的实例和 .swf文件,它实际上被分为很多更小
第十三章334
的子栈。我们首先要分别查看这些子栈,观察它们是如何组合形成主堆栈的。(我们
讨论的栈和在第十一章中所讨论的 LIFO与 FIFO栈并没有直接的关系。)
内部层堆栈
在 Flash制作工具中手动创建的实例存在于一个称为内部层堆栈的栈中。这个栈的
顺序由影片时间线上实际的层来控制。当两个不同时间线层上手动创建的实例发生
重迭的时候,最上层的实例就遮盖了下层的实例。
而且,因为多个剪辑可能存在于同一个时间线层中,内部层堆栈中的每一个层实际
上包含了它自己的小栈。存在于时间线一个层中的重叠剪辑通过Modify(修改)→
Arrange(排列)命令被堆叠到制作工具中。
在Flash 5中,如果两个实例存在于同一个时间线上(也就是说,两个实例的_parent
属性必须相等),我们可以用swapDepths()方法来交换它们在内部层堆栈中的位置。
Flash 5之前的版本中,不能通过 ActionScript来改变内部层堆栈。
程序化生成的剪辑堆栈
程序化生成的剪辑被堆叠在内部层堆栈中,和手动创建的实例相分离。每一个实例
有它自己的程序化生成剪辑堆栈,容纳着通过duplicateMovieClip()和attachMovie()
创建的剪辑。这些剪辑的堆栈顺序根据它们的创建方式而有不同。
通过 attachMovie()生成的剪辑如何添加到堆栈
一个通过attachMovie()生成的新的实例总是堆叠到它所添加到的实例上面(也就是
说相对在前)。例如,假设在影片的内部层结构中有两个剪辑X和 Y,X所在的层在
Y上面。现在假设添加一个新的剪辑 A到 X上,添加一个新剪辑 B到 Y上:
x.attachMovie("A", "A", 0);
y.attachMovie("B", "B", 0);
在我们的假定里,剪辑会按照从上到下的顺序出现:A,X,B,Y,如图 13-1所示。
一旦一个剪辑产生,它还在内容之上提供了一个单独的空间,以程序化地生成剪辑。
也就是说,可以将剪辑添加到已经添加的剪辑。
335影片剪辑
添加的实例 "A"
手动创建的实例 "X"
附加的实例 "B"
手动创建的实例 "Y"
时间线层 2
时间线层 1
图 13-1 一个实例堆栈示意
添加到 Flash文档影片的 _root中的剪辑被放在 _root影片程序化生成剪辑堆栈
中,它会出现在_root影片的所有剪辑之前,甚至在那些包含程序化生成内容的剪
辑之前。
让我们来扩展一下上面的例子。如果我们要将剪辑 C添加到包含剪辑 X,Y,A和 B
的影片的 _root中,那么剪辑 C会出现在所有其他剪辑之前。图 13-2显示了扩展
后的结构。
附加的实例 "A"
手动创建的实例 "X"
附加的实例 "B"
手动创建的实例 "Y"
时间线层 2
时间线层 1
附加的实例 "C"添加到 _root的程序化生成剪辑
图 13-2 将一个实例添加到 _root之后的实例栈
通过 duplicateMovieClip()生成的剪辑如何添加到堆栈
每一个通过duplicateMovieclip()复制的剪辑都被放在程序化堆栈中,和实例的种子
剪辑的创建方式相一致:
第十三章336
● 如果实例的种子剪辑是手动创建的(或者是用 duplicateMovieClip()从手动创
建的剪辑中复制的),那么新的实例就被放到 _root上的堆栈里。
● 另一方面,如果实例的种子剪辑是用 atachMovie()创建的,那么新的剪辑就被
放到它的种子剪辑堆栈中。
我们回到上面的例子来看看它究竟是如何工作的。如果我们通过复制剪辑 X(手动
创建的)而创建出剪辑 D,那么剪辑D就被放到_root上的堆栈,和剪辑 C在一起。
与此相似,如果我们通过复制剪辑 D(从手动创建的剪辑 X而来)而创建出剪辑 E,
那么 E也被放到 _root上的堆栈,和 C与 D在一起。但是如果我们通过复制剪辑 A
(用 attachMovie()创建的)而创建出剪辑 F,那么 F就被放在 X上的堆栈里,和剪
辑 A在一起。可以参见图 13-3中的示意。
附加的实例 "A"
手动创建的实例 "X"
附加的实例 "B"
手动创建的实例 "Y"
时间线层 2
时间线层 1
附加的实例 "C"
添加到 _root
的程序化生成剪辑 复制的实例 "D"
复制的实例 "E"
复制的实例 "F"
图 13-3 包含不同复制剪辑的实例堆栈
在程序化生成剪辑堆栈中给实例的深度赋值
你也许会感到疑惑,是什么确定了图 13-3中剪辑 C,D和 E的堆栈顺序,或者剪辑
A 和 F 的顺序。程序化生成剪辑的堆栈顺序是由传递给 a t t a c h M o v i e ( )或者
duplicateMovieClip()函数的参数 depth来确定的,也可以用 swapDepths()函数在
任何时候进行修改。每一个程序化生成剪辑的 depth(有的时候称为 z-索引)确定
了指定程序化生成堆栈中实例应处的位置。
337影片剪辑
剪辑的 depth可以是任何整数,从底部开始度量,因此,-1在 0的下面,1在 0上
面(也就是在前面),2就更高,依此类推。当两个程序化生成剪辑占据了屏幕上的
同一个位置的时候,depth较大的一个就在另一个之前渲染。
层是单占的结构,每次在同一时间只有一个剪辑能占据堆栈中的一个层 —— 将一
个剪辑放到被占据的层中会代替(删除)该层原来的占据者。
剪辑的深度可以有间隙:可以让一个剪辑在深度 0,另外一个在深度 500,第三个在
深度 1000。深度的赋值出现间隙并不会产生执行问题,也不会造成存储器的浪费。
.swf文档的 _level堆栈
除了内部层堆栈和程序化生成剪辑堆栈之外,还存在第三种(最后一种)堆栈,也
就是文档堆栈(或者说文件级堆栈),它不控制实例的重叠,而控制通过loadMovie()
装载到播放器中的所有 .swf文件。
第一个装载到 Flash 播放器中的 .swf 文件被放在文档堆栈的最底层(用全局属性
_level0来表示)。如果在载入了第一个文档之后再载入任何其他的 .swf文件到播
放器中,就可以将它们设置到文档堆栈中 _level0上面的某一个层中,随意地将它
们放在原来文档之前的某个位置上。文件级堆栈中高层文档中的所有内容将会在低
层文档之前显示,不管每一个文档里的影片剪辑堆栈顺序如何。
正如程序化生成剪辑堆栈在每层只允许存在一个剪辑一样,文档堆栈每层也只允许
有一个文档。如果将一个 .swf文件装载到已经被占据的层级中,该层原来的占据者
就会被新装载的文档所代替。例如,可以通过装载一个新的 .swf文件到 _level0中
而代替原来的文档。装载新的 .swf文件到_level1将会在视觉上遮盖 _level0中的
影片 ,但是并不会把它从播放器中删除。
图 13-4简要地给出了 Flash播放器中所包含的多个文档之间的关系。
堆栈和执行顺序
影片剪辑的分层和时间线层次会影响到代码的执行顺序。规则如下:
第十三章338
FLASH 播放器
Depth 2
Depth 1
Depth 0
La
ye
r 3
添加到 Layer 3剪辑中的实例,
或者其复本
手动创建的实例
La
ye
r 2
添加到 Layer 2剪辑中的实例,
或者其复本
手动创建的实例
La
ye
r 1
添加到 Layer 1剪辑中的实例,
或者其复本
手动创建的实例
程序化
生成的
剪辑堆栈*
内部层
堆栈*
Depth 2
Depth 1
Depth 0
La
ye
r 3
添加到 Layer 3剪辑中的实例,
或者其复本
手动创建的实例
La
ye
r 2
添加到 Layer 2剪辑中的实例,
或者其复本
手动创建的实例
La
ye
r 1
添加到 Layer 1剪辑中的实例,
或者其复本
手动创建的实例
程序化
生成的
剪辑堆栈*
内部层
堆栈
Do
cu
me
nt
Le
ve
l S
tac
k
_level1 (影片B.swf)
_level0 (影片A.swf)
* 程序化生成剪辑堆栈所包含的附加剪辑并不基于附加的
种子剪辑和添加到 _level1._root 的剪辑
图 13-4 完整的 Flash播放器影片剪辑堆栈
● 在不同时间线层中的帧上的代码总是按照从上到下的顺序来执行。
● 当手动创建的实例最初被载入的时候,它们的时间线上和load事件处理器中的
代码按照 Flash文档发布设置中所给出的装载顺序来执行 —— 默认顺序为从
下到上,也可以从上到下。
例如,假设有一个包含两层的时间线,top和 bottom两层, top在层堆栈中位
于 bottom之上。我们将剪辑 X放在 top层上,将剪辑 Y放在 bottom层上。如
果文档的装载顺序被设置为从下到上,那么剪辑Y中的代码就会在剪辑X中的
339影片剪辑
代码之前执行。如果文档的装载顺序被设置为从上到下,那么剪辑X中的代码
就会在剪辑 Y之前执行。这个执行顺序只针对 X和 Y首次出现的帧。
● 一旦载入,影片的所有实例都被设置一个执行顺序,它和载入顺序是相反的,
最近添加到影片的实例,其代码总是首先得到执行。
对这些规则应该谨慎使用。层是变幻不定的,因此,你应该避免依靠相对的关系来
产生代码。要争取让创建的代码能够安全地执行,而不依靠堆栈中剪辑的执行顺序。
我们可以将所有的代码放在 scripts层中,它处于每一个有代码的时间线最顶层,从
而可避免由执行堆栈所引发的一些问题。
实例和主影片的引用
在前面的小节中,我们学习了如何在 Flash播放器中对影片剪辑实例和外部 .swf文
件进行创建和分层。我们必须能够引用这些内容,才能用 ActionScript来有效地控
制它们。
我们在下面四种常见环境中引用实例和主影片:
● 获取或者设置剪辑或影片的属性。
● 创建或调用剪辑或影片的方法。
● 对剪辑或影片提供一些函数。
● 操作剪辑或者影片数据。例如,将它存储在变量中,或者将它作为参数传递给
函数。
进行实例引用的环境和影片的环境尽管很相似,但是,我们用来建立引用的工具却
是各种各样的。我们在本节剩余的部分就要介绍 ActionScript中的实例和影片引用
工具。
使用实例名称
在前边我们学到,影片剪辑是通过名称来引用的。例如:
第十三章340
trace(myVariable); // 引用一个变量
trace(myClip); // 引用一个影片剪辑
为了直接引用一个实例(如前面的 trace()例子所示),这个实例必须存在于代码所
属的时间线上。例如,如果有一个名为 clouds的实例放在文档的主时间线上,就
可以从属于主时间线的代码中引用到 clouds,如下所示:
// 设置实例的一个属性
clouds._alpha=60;
// 调用实例上的一个方法
clouds.play();
// 将实例放进一个其他相关实例构成的数组中
var background=[clouds,sky,mountains];
如果要引用的实例和代码不在同一个时间线上,就必须使用更多精细的语法,这些
语法将在后面介绍。
引用当前实例或影片
在引用剪辑的时候并不总是需要使用实例的名称。属于实例时间线的某帧的代码可
以直接引用该实例的属性和方法,而不用任何实例名称。
例如,要设置剪辑 cloud的属性 _alpha,可以将下面的代码放在 cloud时间线
的某帧上:
_alpha = 60;
类似的,要从cloud时间线的某帧上调用 cloud的 play()方法,可以简单地这样写:
play();
这个技术可以在任何时间线上使用,包括主影片的时间线。例如,如果下面的两个
语句都属于 Flash文档主时间线上的某一个帧,那么它们就是同义的。第一个语句
隐式地指向主时间线,而第二个则通过全局属性 _root显式地指向主影片:
gotoAndStop(20);
_root.gotoAndStop(20);
正如我们在第十章中所学到的,一个实例的事件处理器上的代码和时间线代码一样,
也直接指向属性和方法。例如,我们可以将下面的事件处理器添加到 cloud中。这
341影片剪辑
个处理器设置 cloud的一个属性,然后调用 cloud的一个方法,而不显式地指向
cloud实例:
onClipEvent(load) {
_alpha = 60;
stop();
}
但是,不是所有的方法都可以隐式地指向一个影片剪辑。任何同对象的全局函数同
名的影片剪辑方法(比如duplicateMovieClip()),都必须用显式的实例引用来调用。
因此,在不太明确的时候就要使用显式引用。我们在后面将会讨论有关方法和全局
函数矛盾的更多问题。
使用 this关键字的自引用
当我们要从某个实例时间线上的某帧,或者从它的某个事件处理器中显式地引用当
前实例的时候,可以使用this关键字。例如,下面的语句如果属于 cloud实例时
间线上的某帧,它们就是同义的:
_alpha = 60; // 对当前时间线的隐式引用
this._alpha = 60; // 对当前时间线的显式引用
当我们能够直接指向某个剪辑的时候还要使用this来指向该剪辑有两个原因:如果
不使用显式实例引用,特定的影片剪辑方法就会被解释程序误认为是全局函数;如
果我们遗漏了this引用,解释程序就会以为我们要调用类似的全局函数,然后告诉
我们遗漏了“target”影片剪辑参数。要解决这个问题,我们使用this,如下所示:
this.duplicateMovieClip("newClouds", 0); // 在实例上调用一个方法
// 如果我们遗漏了 this引用,就会引发错误
duplicateMovieClip("newClouds", 0); // 噢!
使用this,可以方便地将一个引用传递给在影片剪辑上操作的函数所在的时间线:
// 这里是一个对剪辑进行操作的函数
function moveTo(theClip, x, y) {
theClip._x = x;
theClip._y = y;
}
// 现在我们在当前时间线上调用它
moveTo(this, 150, 125);
第十三章342
如果我们要进行很多的面向对象编程工作,那么在使用 this关键字来引用实例和
影片的时候要小心。记住,在一个自定义的方法或者对象构造器内,this的意思
有很大不同,它不是对当前时间线的引用。参见第十二章可以获得相关的细节内容。
引用嵌套实例
正如我们在本章的介绍中所学到的,影片剪辑实例通常会嵌套在另外的实例中。也
就是说,一个剪辑可以包含另外剪辑的实例,这个实例本身又包含其他剪辑的实例。
例如,一个游戏中的 spaceship 剪辑可以包含 blinkingLights 剪辑或者
burningFuel剪辑的实例。或者一个字符的face剪辑可以包含单独的eyes,nose
和 mouth剪辑。
在前面我们简要地介绍了如何在剪辑实例的层次中的任何一点开始进行向上或向下
的导航,就和你对硬盘上的一系列子目录进行上下导航一样。现在,我们来看看其
中的细节内容,以及一些实例。
我们首先来考虑如何指向一个嵌套在当前实例内的实例。当一个剪辑放在其他剪辑
的时间线上时,它就成为那个剪辑的属性,可以像访问任何对象属性那样访问它(使
用点操作符)。例如,假设将 clipB放在 clipA的画布上。要从 clipA的时间线
上访问 clipB,我们使用对 clipB的直接引用:
clipB._x = 30;
现在假设clipB包含另外一个实例clipC。要从clipA的时间线上的某一帧指向
clipC,我们将 clipC作为 clipB的一个属性来访问,如下所示:
clipB.clipC.play();
clipB.clipC._x = 20;
很不错,不是吗?这种方法的扩展性是无限的。因为每个放在其他剪辑时间线上的
剪辑实例都变成其主剪辑的属性,可以用点操作符来分离各个实例,从而贯穿这个
层次,如下所示:
clipA.clipB.clipC.clipD.gotoAndStop(5);
343影片剪辑
既然我们已经知道如何在实例层次中向下导航,现在,我们来看看如何向上导航,
指向包含当前实例的实例或者影片。正如我们在前面所看到的,每一个实例都有一
个内置的 _parent属性,它指向包含这个实例的剪辑或主影片。我们使用_parent
属性,如下所示:
myClip._parent
回忆一下我们刚才使用的clipA在主时间线上的例子,clipB在clipA内,而clipC
在 clipB内,我们来看看如何使用 _parent和点操作符来指向层次中不同的剪辑。
假设下面的代码是放在 clipB时间线的某一帧中的:
_parent // 一个对clipA的引用
this // 一个对clipB(当前剪辑)的引用
this._parent // 对 clipA的另外一个引用
// 哦,我喜欢这种方式,我们再来试试⋯⋯
_parent._parent // 一个对clipA的父引用(clipB的祖父),
// 也就是本例中的主时间线
注意,下面的代码虽然是合法的,但是如果用指向 clipB的clipC属性的引用在层
次中向下导航只是为了用 _parent向上导航的话,就是一种没有必要的迂回。这些
迂回的引用虽然没有必要,但是它的确显示了点号的灵活性:
clipC._parent // 一个指向 clipB(当前时间线)的迂回引用
clipC._parent._parent._parent // 一个指向主时间线的迂回引用
请注意如何使用点操作符来在剪辑层次中下降,以及如何使用 _parent属性来上
升。如果这些内容对你来说太生疏,你也许应该尝试在Flash中建立clipA,clipB,
clipC层次,使用我们的例子中的代码。恰当的剪辑引用是 ActionScript编程的基
本技巧之一。
注意,剪辑的层次就像是一棵家族树。和那种每个后代都有两个双亲的两性繁殖不
同,我们的剪辑家族树被扩展为无性的。也就是说,每一个后代只有一个单亲,这
个单亲可以有很多孩子。任何剪辑(也就是树中的任何节点)都可以有一个且只有
一个父亲(包含它的剪辑),但是可以有多个孩子(它所包含的剪辑)。当然,每一
个剪辑的父亲依次也可以有一个单独的父亲,这意味着每一个剪辑只能有一个祖父
(不像人类那样有四个祖辈)。参见图 13-5。
第十三章344
主时间线
_root
clipD clipA
clipB
clipC
_root 是:
clipA的父亲
clipD的父亲
clipD 是:
_root的儿子
clipA的兄弟
clipA 是:
_root的儿子
clipB的父亲
clipC的祖父
clipD的兄弟
clipB 是:
clipA的儿子
clipC的父亲
clipC 是:
clipB的儿子
clipA的孙子
图 13-5 剪辑层次的示意
因此,不管你在家族树中有多深入,如果你按照相同的步骤向上走,就必定会回到
原来开始的地方,如果你向下走是为了向上走的话,就毫无意义。但是,先在层次
中上升,然后按照不同的路径向下走就不是没有意义了。例如,假设主时间线也包
含 clipD,那么 clipA和 clipD就算是兄弟,因为它们两个的 _parent都是主
时间线。在这个例子中,可以从属于clipB的脚本这样来指向 clipD,如下所示:
_parent._parent.clipD // 这将指向 clipD,主时间线(clipA的 _parent)
// 的一个孩子,因此它是clipA的兄弟
注意,主时间线没有 _parent属性(主影片是任何剪辑层次的顶层,不能被包含在
其他时间线中),要指向 _root._parent只会得到 undefined。
用 _root和 _leveln指向主影片
既然我们已经了解了如何在层次中进行和当前剪辑相关的向上和向下的导航,现在,
我们就来看看沿着绝对路径,或在存储于播放器文档堆栈的其他层级中的其他文档
之间进行导航的方法。在前边的章节中,我们看到了这些技术在变量和函数上的应
用,在这里,我们将学习它们是如何来控制影片剪辑的。
用 _root指向当前层级的主影片
当一个实例在剪辑层次中嵌套得很深的时候,我们可以重复使用 _parent属性来
345影片剪辑
在层次中上升,直到到达主影片时间线。但是,为了减少从深层嵌套剪辑指向主时
间线的麻烦,我们也可以使用内置的全局属性_root,它是指向主影片时间线的简
短引用。例如,我们现在要播放主影片:
_root.play();
_root属性被称为剪辑层次中某个已知点的绝对路径,因为它和与当前剪辑相关的
_parent以及this属性不同,_root属性不管从哪个剪辑开始引用都一样。下面
这些引用都是等价的:
_parent._root
this._root
_root
因此,当你不知道一个给定的剪辑在层次内嵌套在什么地方的时候,你可以,也应
该使用 _root。例如,考虑下面的层次结构,circle是主影片时间线的孩子,而
square是circle的孩子:
main timeline
circle
square
现在考虑下面这个在 circle和 square的帧中都有的脚本:
_parent._x += 10 // 将这个剪辑的父剪辑向右移动 10个像素
当这些代码从 circle内执行的时候,它会让主影片向右移动 10个像素。当它从
square内执行的时候,会让 circle(不是主影片)向右移动 10个像素。为了让脚
本不管在哪执行都可以使主影片向右移动 10个像素,我们可以这样写:
_root._x += 10 //
本文档为【Flsh 影片剪辑】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。