三、深度(Depth)
深度(Depth)仅仅需要一个数字描述,Flash Player 使用这个数字来指定对象的层叠位置。所谓层叠是指一种显示时的的遮盖关系。深度值较大则显示较前,深度值较小则显示较后,较后的要被较前的遮盖。深度具有可比性但仅限于兄弟节点,所以同一父节点下的兄弟具有不同的深度,同时这个深度也描述了他们之间的层叠关系。属于不同父节点的子节点可以有相同的深度,但他们不具有可比性。
编辑环境下,Flash使用了图层(Layer) 来组织对象。这时, Flash 并没有给对象指定一个深度,只有当Flash 文档在运行时 Flash Player根据用户最初在编辑环境下的拖放情况来指定对象的深度。这里的图层可以认为是同一父时间轴节点下子时间轴节点的集合。图层和图层之间不存在交集,而且图层是一个动态的集合,随着程序的运行,集合中的元素也会发生变化。Flash 也可以在运行时动态地创建时间轴,例如使用 attachMovie()、duplicateMovieClip()等语句,但必需以参数的形式给出要被创建的时间轴的深度。
对象在编辑环境下的拖放顺序以及时间轴上图层的层叠关系,用来帮助Flash 确定对象的深度。图层在Actionscript中是不被引用的,所以在swf文件中也不会有图层这个概念。在编译时,图层被分解为多个对象(多个时间轴),并且每一个对象都会被指定一个深度。当Flash 播放时,时间轴会读取图层中的内容,并根据和其他对象的相对位置关系指定它的深度。如果对象所在的图层较高则获得一个比较高的深度,如果对象所在的图层较底则获取一个较低的深度。与此同时,Flash也会为对象指定一个名称,实现脚本对该对象的引用。深度概念也影响Flash 中对象的创建,在FileàPublish SettingàLoad Order 中可以设置对象被创建的顺序,指定是由低深度到高深度(Bottom up)地创建,还是由高深度到低深度(Top down)地创建,Flash中的默认为前者。
Actionscript 还提供了一些与深度相关的方法和管理对象:
深度方法或深度管理对象
功能描述
返回值
MovieClip.attachMovie()
从库中创建一个电影剪辑实例。
被创建的电影剪辑的引用
MovieClip.createEmptyMovieClip()
创建一个空的电影剪辑
被创建的电影剪辑的引用
MovieClip.createTextField()
创建一个文本框
无返回值
MovieClip.duplicateMovieClip()
创建一个电影剪辑的拷贝
被创建的电影剪辑的引用
MovieClip.getDepth()
返回电影剪辑的深度
深度数值
MovieClip.getInstanceAtDepth()
返回占据指定深度的电影剪辑
指定深度下电影剪辑的引用
MovieClip.getNextHighestDepth()
返回最高的可用深度
深度数值
MovieClip.removeMovieClip()
移除动态创建的电影剪辑
无返回值
MovieClip.swapDepths()
交换两个电影剪辑的深度
无返回值
DepthManager Component
深度管理器
深度方法和功能描述
这里需要加以说明的是,SwapDepth()方法可以把在不同深度的两个对象进行深度交换。如果要实现这种交换有两种方法:一是把两个对象进行调换,即把上面的拿到下面,把下面的拿到上面,这是对对象本身进行操作;二是直接更改对象的深度属性,即直接修改指定对象深度的数值。但是如果使用后者显然不是在交换深度而是在设置深度了。SwapDepth()方法是实现对象深度位置的交换而非设置深度。可以把深度理解为一个容器,比如说是房子,如果两个人要交换房子,那么必须是你搬过来我搬过去,而仅仅更换门牌是没有什么用处的。所以深度是只读的。另外,getDepth()方法可以很方便的获取MovieClip、Button和TextField对象的深度数据。
另外,深度管理器(DepthManager)是一个专门对深度进行管理的Flash V2组件,它封装了若干深度管理的方法:
DepthManager.createChildAtDepth():在指定深度处创建指定元件的子级。
DepthManager.createClassChildAtDepth():在该指定深度处创建指定类的对象。
DepthManager.createClassObjectAtDepth():在指定深度处创建指定类的实例。
DepthManager.createObjectAtDepth():在指定深度处创建一个对象。
DepthManager.setDepthAbove():移动实例到指定实例的深度之上。
DepthManager.setDepthBelow():移动实例到指定实例的深度之下。
DepthManager.setDepthTo():将实例移动到指深度。
四、深度区域:
在为对象指定深度时必须清楚地了解Flash在深度上的三个区域。在Flash 中,深度的范围在-16384到2130690045之间,任何Flash的可视对象都不会超出这个范围。在播放时,Flash把时间轴中的对象按照默认的设置进行加载。-16384到-1是深度的第一个范围,在这个深度区域对象不可以使用脚本创建和移除,用于放置用户在编辑环境下创建的对象,这里把它称为时间轴区(Timeline)。接下来是0到1048575的深度范围,在这个深度范围内,对象可以使用脚本创建和移除,所以称为动态区(Dynamic)。之后一个区域称为保留区(Reserve),深度范围在1048576和2130690045之间,可以放置MC但是不可以移除。
Flash的深度区域:
区域
深度范围
特征
时间轴区
-16384 到 -1
主要用于放置Flash 编辑时的对象,且对象不可用脚本移除,但可以使用脚本创建。
动态区
0到1048575
实现运行时的动态创建、动态访问和动态移除
保留区
1048576到2130690045
实现运行时的动态创建、动态访问,但不支持使用脚本移除对象。
Flash 在动态区之前提供了时间轴区,用于放置编辑环境下时间轴中的对象。在编辑环境下,最底层的对象放置到区域的最深处——-16384。在通常情况下会从-16383这个深度开始,Flash依次向较高深度放置时间轴中的对象。
接下来是动态区,在这个区域可以放置那些可以动态创建和移除的对象。而且这个区域也符合一般人的使用习惯,因为它的深度是从0开始的,而开发者在创建对象时通常也会考虑这个范围。在这个范围可以获得比较大的自由权,开发者可以使用脚本动态地创建和移除对象。如果对象不在动态区,那么首先可以把对象交换到动态区,然后再进行移除。
保留区可以理解为动态区的一个扩展,只是在保留区中无法使用脚本移除对象,对象在那个区域可以动态地被创建。在这个区域对象也可以使用swapDepth()方法交换到其它的深度区域。但保留区还是一个比较特殊的区域,因为那里的对象不可以被移除,而且深度高的有点离谱。在保留区Flash为对象提供了一定的存储空间。
需要特别注意的是,Flash 在使用createEmptyMovieClip()方法创建的空MC时,深度的分配上几乎没有什么限制,深度可以指定在-999999999999以下或是999999999999以上。虽然这个深度已经超出了刚才讨论的那个范围。
五、时间轴刷新:
关于深度,可能会遇到一些另人混淆的问题,特别是动态区和时间轴区。当时间轴被刷新时,所有的对象将被重新创建。Flash 会扫描位于该时间轴的时间轴区域,并清除保存在这些深度上的对象。然后Flash 读取时间轴上的数据并重新创建这些对象,但是通过动态方法创建的对象将被忽略。其中一个例子就是使用gotoAndPlay()返回播放已经播放过的帧,例如从第10帧返回到底5帧播放,这时Flash为了重新展示第5帧的内容必须刷新时间轴并完全重构第5帧的内容。但是使用动态方法创建的对象如果在时间轴刷新时被移除,那么这时并不被自动创建,除非程序给出。
很明显,这样可能会引起一些问题。首先,这样会失去在时间轴区域或是使用动态方法已经创建并交换到时间轴区域的对象。也就是在这中情况,不可以把使用动态方法创建的对象交换到时间轴区域,否则在时间轴刷新时会丢失这些对象。在开发时为避免这种情况的发生,可以创建一个单帧停止的MC,这样的MC可以是安全的,可以不必为时间轴刷新而担忧。
另外一个问题是无法移除所需要移除的对象。其原因是由于使用深度交换方法把时间轴区的对象转移到了非时间轴区,这样在时间轴刷新时就无法清除该对象了,而且这次刷新还会创建一个新的该对象到时间轴区。这种情况也应该避免发生。
还有一个需要清楚的问题是场景(Scene)跳转的问题。场景间的跳转不会引起时间轴的刷新,也不会触发其他一些特殊的事件。作为场景本身而言,它们是主时间轴的片段,当Flash被发布以后,这些场景便会整合到一起,变成单个的主时间轴。所以用户在一个场景创建的对象,它仍旧可以在接下来的场景出现,当然也可以使用脚本使那些对象消失。
六、播放器执行顺序:
在深度问题上,还有一点比较容易被人们忽视。正如前面所提到的,深度也会影响到脚本的执行顺序。事实上深度本身并不影响脚本的执行顺序,但是深度却与对象的创建顺序有关。一般我们都会认为,在同一时间轴上,首先被创建的对象会首先执行它所包含的脚本。另人感到奇怪的是,在默认的发布设置下,那些最晚被创建的对象反而首先执行了EnterFrame事件中的脚本。在默认情况下对象从最底层开始被创建,这一点我们可以在Flash的发布选项中设置。帧脚本是按照它们被创建的顺序执行的。这样如果在bottom up 模式下,从技术角度而言,较低层的脚本反而较早执行,但是值得一提的是,EnterFrame事件中的脚本刚刚与之相反。
看来很有必要追究一下EnterFrame事件到底是怎样的一个事件。EnterFrame事件随着电影剪辑的帧速被持续地触发。可以认为EnterFrame事件是在播放头进入某一帧时触发的一个事件,但是在EnterFrame事件中的代码将先与帧脚本执行。但是,对象的创建和Flash的播放(对象状态的改变过程)到底是怎样一个关系呢?在Flash中第一帧的帧脚本和其它帧的帧脚本有着不同的行为。当运行一个Swf文件时,它的第一帧将首先被创建,然后开始载入该文档中的对象并拖入到舞台,这个过程可以认为是一个初始化的过程。所以在这个过程中随着对象的载入第一帧的容量将不断增大。这样做的原因也是显而易见的,只有当对象全部载入后,脚本才能执行,设想如果脚本所要操作的对象都不存在,那么脚本必然会发生错误。一旦所有的对象载入完毕,除第一帧外的其它帧都将被一次性创建,并开始播放。这样做的另外一个原因是,如果按照先创建,先播放的原则,那么必然导致不同电影剪辑播放时步调的不一致。因为创建对象是需要时间的,特别是在网络上,它还要有一个下载的过程。这里还要注意的是#initclip和#endinitclip中的代码块,它们通常定义在电影剪辑的第一帧,只要符号被定义且被编译(电影剪辑符号定义了,但不一定都被编译),那么就执行该代码段,它用于电影剪辑的初始化,所以优先于其它脚本执行,并且仅执行一次,它的运行效果将作用到该剪辑的所有实例。
这样,就可以得出,Flash的执行可以分成两个过程。首先是对象的下载、创建和初始化,接下来是一个播放的过程。播放是一个对象状态随着帧速改变的过程,其状态的两个极端是存在与不存在。这样在Flash的执行过程中,电影剪辑第一帧的脚本将首先被执行;然后是播放过程,其脚本执行的原则是EnterFrane事件脚本的执行先于帧脚本的执行。
七、图层(Layer)、层级(level)和深度(depth)
通常人们会把图层和层级混淆起来使用,但事实上它们是两个完全不同的概念。时间轴可以以两种方式独立于主时间轴:电影剪辑(Movie Clip)和层级(Level)。这里所谓的独立是指独立的播放与停止。图层(Layer),只在编辑环境下被看到,用于存放和组织在时间轴上的对象。它们仅仅只是在编辑环境下出现,当发布一个Flash文件时,这些图层将被压缩到同一个时间轴上。
层级(Level)位于一个独立的时间轴上,用于存储使用loadmovie方法加载的外部Swf文件。层级(Level)可以使用_level0, _level1, _level3, _leveln 对其进行引用。层级越高,就越接近观察者,也就越容易被看到,这一点与深度是一致的。使用该方法加载的电影将贯穿电影的主时间轴。由于这个原因从外部加载电影的目的常常是为了加载某些声音,因为声音不会影响到原来电影的播放。
电影剪辑(Movie Clip)同样是一个独立的时间轴,但是电影剪辑位于库(Library)中,并不是位于电影的外部。所以它与层级不同,如果需要的话,你可以在帧上创建一个电影剪辑的实例。 从库中拖动一个电影剪辑到舞台(Stage),那么就创建了该电影剪辑的一个实例,可以指定一个唯一的名称对其进行引用。电影剪辑可以包含其它的电影剪辑,这就是通常所说的嵌套(nesting)。
深度(Depth)通常会用于swapDepth()和attachMovie()这些方法,用于指定电影剪辑的层叠顺序。它可以被认为可视对象在时间轴上的层叠顺序(Z-Order)。
这里还要指出的是 _root 和 _level0 的区别。_root 和_level0引用的并非同一个对象,它们是有差别的。_root 指代电影的主时间轴,如果一个电影有多个层级那么_root指代当前脚本正在执行的那个层级。例如:在层级n中的某个脚本包含了 _root,这时_root返回 _leveln,_root和_leveln才是等同的。