如果是以往的Flash开发的话,在第一帧写上显示加载进度的代码,然后将所有导出素材通过一些手段,让其不在第一帧导出。这样应该能在程序一开始就显示“加载进度”。但是像我这种,用Flash IDE只是方便存放素材,所有代码都写在as文件里的,这个方法就不那么凑效了。
尽管可以将声音、图片等素材放在第二帧导出,同时文档类的体积也很小。但是由于文档类会通过import语句导入其他类,例如自己写的menu类。又如果,这个导入的menu类又通过import语句导入其他的类,好像我之前那样为了让menu有3d效果导入了pv3d的话。编译起来,第一帧的大小还是相当可观的(我当时编译的时候,差不多有100k,都是pv3d的东西)。即是说,用文档类的方式来写Flash应用的话,会将所有类都编译到第一帧里面。体积自然会大了。
当时想到的解决方法就是要移除文档类的import语句。然后网上一搜,发现bit-101早在N年前就讲了类似的方法。他那里针对的是纯AS3项目的,通过用Flex里面的一个Frame标签,使生成的swf包含两帧。那篇文章后面的comment里面,有人说会将Flex框架也带进编译出来的swf文件里,从而增大体积。不过我自己测试了一下,也没发现有这个问题。只是由于用了Embed标签,增加了几个Flex的类。
不过他说的方法只对纯AS3有效。下面说一说用Flash IDE编译swf的时候,制作preloader的技巧(这个只能算技巧,而不能算是好的编程习惯,个人认为)
1.新建一个fla,文档类设置为com.example.Main.保存在project文件夹里。
2.编写文档类Main.as,保存在project文件夹下面的com\example\里面。
package com.example
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
public function onEnterFrame(e:Event):void
{
if(framesLoaded == totalFrames)
{
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
nextFrame();
var MainContentClass:Class = getDefinitionByName("com.example.MainContent") as Class;
var mainContent:Sprite = new MainContentClass();
addChild(mainContent);
}else{/*这里写现实进度的代码*/}
}
}
}
==================
package com.example
{
import flash.display.MovieClip;
public class MainContent extends MovieClip
{
public function MainContent()
{
/*在这里做想做的事情*/
}
}
}
======================
4.在FlashIDE里面,添加一个名为mainContent的影片剪辑,导出为com.example.MainContent,选择在第二帧导出(Flash CS4可以在项目设置里面设置成在第二帧导出,FlashCS3的话,就取消导出,然后拖到舞台的第二帧强制导出)。
5.编译测试。
说明:在第二步里面,我在文档类里通过getDefinitionByName()来获取MainContent类,这样就可以编译的时候不用在Main类里导入MainContent了。之所以要进行第四步是因为,如果不这样的话,MainContent压根不会被编译进swf里,整个swf文件就只有一个Main类。然后getDefinitionByName()的时候会出错。
-----------------
**************
注意事项:因为库元件都在第2帧导入,而文档类在第一帧就开始构造了,所以在预加载完成前,不要实例化库中的类,如 new 库元件类(),否则不能正确建立类实例;但可以声明库元件的类变量。
======================================
在之前一篇文章Preloader in Flash CS4里,我讲了怎么创建一个正常工作的preloader。今天重新用上了FlashDevelop,发现它有一个preloader的模板,研究了一下。结果,发现原来这个是如此简单的事情。
实际上制作Flash Preloader的时候,实际上就只有一个问题。由于preloader本身要很快显示出来,所以它的体积要尽可能地小。另一方面,要减少preloader的体积,就不能导入其他的类,但却要保证其他类在编译的时候要导出。之前那篇文章里面的核心就是利用Flash IDE库中元件在第二帧导出,以及跟Main类绑定,从而使得Main类能在编译的时候被导出。
这次说一下纯AS3项目下更加简单的方法。
Bit101提供的方法是利用frame这一Metatag,使编译的时候生成两帧。不过感觉上还是有些不容易理解。下面是FD里的做法。
01 |
package |
02 |
{ |
03 |
import flash.display.DisplayObject;
|
04 |
import flash.display.MovieClip;
|
05 |
import flash.events.Event;
|
06 |
import flash.events.ProgressEvent;
|
07 |
import flash.utils.getDefinitionByName;
|
08 |
|
09 |
public class Preloader extends MovieClip
|
10 |
{
|
11 |
|
12 |
public function Preloader()
|
13 |
{
|
14 |
addEventListener(Event.ENTER_FRAME, checkFrame);
|
15 |
loaderInfo.addEventListener(ProgressEvent.PROGRESS, progress);
|
16 |
// show loader
|
17 |
}
|
18 |
|
19 |
private function progress(e:ProgressEvent):void {/*update loader*/}
|
20 |
|
21 |
private function checkFrame(e:Event):void
|
22 |
{
|
23 |
if (currentFrame == totalFrames)
|
24 |
{
|
25 |
removeEventListener(Event.ENTER_FRAME, checkFrame);
|
26 |
startup();
|
27 |
}
|
28 |
}
|
29 |
|
30 |
private function startup():void
|
31 |
{
|
32 |
// hide loader
|
33 |
stop();
|
34 |
loaderInfo.removeEventListener(ProgressEvent.PROGRESS, progress);
|
35 |
var mainClass:Class = getDefinitionByName("Main") as Class;
|
36 |
addChild(new mainClass() as DisplayObject);
|
37 |
}
|
38 |
}
|
39 |
} |
上面的是FD自动生成的Preloader类。注意的是,这里依然利用getDefinitionByName()来获得Main类,这样就不用导入Main类了。但是如果光这样编译,Main类依然没法被编译进swf中。这时需要在编译的时候添加额外的编译参数
1 |
-frame start Main |
这句话等同frame标签,具体的作用是,新建一个名为start的帧,并将Main类绑定到这个帧上面。
———-华丽分割线———-
还有另外一种方法,不用跟帧打交道的。直接添加下面这个编译参数,这样就强制导出Main类:
1 |
-includes Main |
--------------------------------------------------------
Flash CS4中对AS 3的Export classes in frame设置较AS 2发生的变化
blog挂了几天,终于恢复了。前几天看了一下教程里的RSL使用,引发了一些思考,尝试以后才发现AS 3中对导出类设置相对AS 2发生了变化,真是粗心啊,这么重要的变化之前竟然一无所知,这直接影响swf的proloading。还好这次偶然的机会没有错过,又深刻体验到了一个道理:很小的一个现象也许隐藏着深刻的内涵。下面就把AS 3和AS 2中设置类在第几帧导出的规则总结一下。
在Flash中,对于AS 2设置了Export classes in frame “N”,那么自定义的类就一定是在main time line的第N帧才导出,之前的帧自定义的class都不可用。
但是对于AS 3如果设置了Export classes in frame “N”,那么并不一定所有自定义的类都在第N帧导出,有的可能已经在第一帧就导出了。以下几种情况会使自定义的类在第一帧就导出,而不是在设定的第N帧导出:
- 如果main time line 使用了类,那么类就会在第1帧被导出。这里的“使用”包含创建类的对象、调用类的静态方法、使用类赋值给其它变量等,还包含使用类声明变量的类型,但不包含import类。
- 如果在Flash的publish setting中设置了automaticly declare stage instance,那么main time line中所有设定了instance name对象所绑定的类都会在第1帧被导出。
其余没有被main time line关联到的类,会在publish setting中的Export classes in frame “N”设置所指定的main time line的第N帧导出。因此如果main time line使用了太多自定义的类,就会造成这些类都会在第一帧导出,从而增大第一帧加载的时间,这也将影响proloading的使用。认识到了AS 3中类导出设置的变化,那么我想是不是需要重新考虑一下代码在main time line上的布置了?尽量不要在main time line上使用不必要的自定义类吧,我想这就是这次思考的收获!
========================================
http://sierakowski.eu/list-of-tips/45--two-ways-of-preloading-in-actionscript-3.html
=======================================
flashplayer的缓存目录是:XP系统下是C:\Documents and Settings\用户名\Application Data\Adobe\Flash Player\AssetCache\H7UC3H3Y,VISTA和WIN7系统下是C:\Users\用户名\AppData\Roaming\Adobe\Flash Player\AssetCache\RAU4Y963,这两个路径的最后一个文件夹名是随机的,另外拷贝过去的swz文件也需注意,比如FlexSDK3.2在缓存中的swz文件名是1C04C61346A1FA3139A37D860ED92632AA13DECF.swz,这个文件需要从一个有flashplayer缓存的机子上拷贝到有问题的机子上去。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zlxluofeng/archive/2010/04/29/5543811.aspx
=======================================
创建运行时共享库(Runtime Shared Library,RSL)并不是什么难事也不是新鲜事了,就是把类定义放到一个swf文件里,其它swf文件在运行时可以共享那些类定义。这样可以防止类定义重复从而减小文件大小。RSL有分带Adobe签名的(例如 Flex framework),也有用户自创建的。带Adobe签名的RSL,假如是swz格式,则可以永久保存在客户端计算机的特定目录下(例如 C:\Documents and Settings\Administrator\Application Data\Adobe\Flash Player\AssetCache)。用户创建的,通常只能是swf格式,只能保存在缓存里。接下来你可以先下载源码看一看,然后继续往后阅读。
下载Flash Builder项目:CustomRSL
如上图所示,我在Flash Builder里创建了一个ActionScript 项目,cn.riahome.module 包里的“Libs_RSL.as”已构建为项目的模块,它将被编译为独立的 Libs_RSL.swf 文件(要构建某个*.as文件作为模块,只需“属性->ActionScript 模块->添加”)。这个 Libs_RSL.swf 文件将会存放 cn.riahome.classes 包里所有的类的定义,包括 Adobe 类、Microsoft 类和Sun 类。默认包里的主文件 CustomRSL.as 不需要包含这些类定义(就是不需要 import 那些类),从而减小编译后的 CustomRSL.swf 文件大小。
CustomRSL 类如何使用那些没有 import 的类呢?方法也很简单,只需要把已包含那些类定义的 Libs_RSL.swf 文件加载到 CustomRSL.swf 里,再利用getDefinitionByName()方法取得那些类的引用。注意:加载 Libs_RSL.swf 文件时,必须指明它的应用程序域和CustomRSL.swf的一样,即在加载时,传递给Loader.load()方法的第二个参数为 ApplicationDomain.currentDomain 。关于 ApplicationDomain 类的使用,请查看官方的《Adobe ® ActionScript® 3.0编程》
说白了,就是把类定义划分到别的 swf 文件里,在需要时加载这个 swf 文件,再利用“反射”取得类的引用。不过,值得特别注意的是:这种方法不易于对加载进来的swf进行垃圾回收。除非你加载了swf文件后,并没有引用过里面的东西,否则整个swf都不会被垃圾回收。
其实,假如你熟悉AS3的“反射”,了解 ApplicationDomain 类,并且对 Loader 类了如指掌,那么你能实现的功能远不止这些。
----------------------------------------------------------
但是,就是这么一个基本的需求,在 AS3 中却几乎成了不可能的任务。
在网上搜索“AS3 进度条”,只能看到一些 Flex 的东西,和加载其他文件的方法,几乎没有看到自加载进度条的信息。其实有一个帖子谈到了这个问题,并且楼主也在15楼给出了一个解决方案,不过,他忽略的一点是,AS3中的自加载进度条之所以困难,是因为如果不选择 MovieClip 的“Export classes in frame”选项的话,在后续的操作中,扩展类和 MovieClip 本身将会脱节,也就是直接拖放到场景中的 MovieClip 将是没有扩展类代码的,而直接用代码 new 相应的类的话,也会出现无法使用 MovieClip 上的命名元素的错误。而在 AS3 中,几乎不可能不使用扩展类,因为按钮事件不再允许直接编写,而必须位于扩展类中。
于是搜索“as3 preloder progress”,找到了bit-101 的一篇文章,文中提到的方法很有趣,不过我使用这种方法在 Flash CS3 中编译的时候,却并不成功,看来是不支持自定义属性(自定义属性是 .Net 中的叫法,Java 中叫标注,ActionScript 中叫什么呢?),再看文中的说明,应该是需要 Flex 的编译器 mxmlc,嗯,好像还是不适用啊。
继续搜索,找到了一个提出相同问题的文章,在它的回复里,Den Ivanov 给出了一个
回复中,Den Ivanov 还提到了它的实现原理:
just use:
var programClass:Class = loaderInfo.applicationDomain.getDefinition(”Program”) as Class;
var program:Sprite = new programClass() as Sprite;
addChild(program);
instead of NOT working code:
/*
var program:Program = new Program();
addChild(program);
*/
于是,在自己的程序里使用上述代码,编译运行,出现“未找到符号 Program”的错误,再看 Den 的例子程序,似乎也和我写的一模一样,究竟哪里出了问题呢?
经过一番推敲,最终着落在 embedClip 上,这是一个奇怪的 Clip,在它上面,有 Program、box 和 star 的符号,而且被安排在第二帧,在运行的程序里却完全找不到它的踪迹,在第二帧里删除它,再编译运行例子程序,也出现了“未找到符号 Program”的错误。这么说来,它是用来保证所有的 MovieClip 也在第二帧被加载的……
OK,这样就清楚了,在我自己的程序里,也加入一个类似的 Clip,命名为 ClipLoader,把所有关联了扩展类的 MovieClip 加入它的第二帧,在第一帧中写入代码“stop();”,把它放入场景的第二帧里,在第三帧里把它删除。再次编译运行程序,成功!
回头来想 loaderInfo.applicationDomain.getDefinition 觉得很奇怪,因为我在其他类里面也可以直接使用代码 new 一个和 MovieClip 关联的扩展类的,为什么对于 Program 却需要呢?删除它,直接使用代码 new,确实有问题,话说回来,既然可以用 getDefinition 找到,却无法直接被代码 new 出来,实在很纳闷。不过再想想,向场景加入 MovieClip 的方法,除了用代码 new,直接拖放其实更简单、直观,而且可以进行一些其他的编辑操作,用它是否可行呢?于是把 getDefinition 的相关代码删除,直接向场景的第三帧中加入 Program,编译运行,成功!
我的文档类的代码如下:
01 |
package { |
02 |
import flash.display.Sprite;
|
03 |
import flash.display.MovieClip;
|
04 |
import flash.display.Shape;
|
05 |
import flash.text.TextFormat;
|
06 |
import flash.text.TextField;
|
07 |
import flash.events.*;
|
08 |
|
09 |
public class Reversi extends MovieClip {
|
10 |
private var loadInfo:MovieClip;
|
11 |
|
12 |
public function Reversi(){
|
13 |
super();
|
14 |
var _lineLen:Number = 200;
|
15 |
var _lineH:Number = 3;
|
16 |
|
17 |
loadInfo = new MovieClip();
|
18 |
addChild(loadInfo);
|
19 |
var shape:Shape = new Shape();
|
20 |
loadInfo.addChild(shape);
|
21 |
shape.graphics.lineStyle(1, 0x0000FF, 20);
|
22 |
shape.graphics.moveTo(0, 0);
|
23 |
shape.graphics.lineTo(_lineLen, 0);
|
24 |
shape.graphics.lineTo(_lineLen, _lineH);
|
25 |
shape.graphics.lineTo(0, _lineH);
|
26 |
shape.graphics.lineTo(0, 0);
|
27 |
|
28 |
var txtFormat:TextFormat = new TextFormat("Arial", null, 0x888888, true);
|
29 |
var txt:TextField = new TextField();
|
30 |
txt.autoSize = "center";
|
31 |
txt.x = 100;
|
32 |
loadInfo.addChild(txt);
|
33 |
|
34 |
loadInfo.x = (this.stage.stageWidth - _lineLen)/2;
|
35 |
loadInfo.y = this.stage.stageHeight/2;
|
36 |
|
37 |
this.loaderInfo.addEventListener(ProgressEvent.PROGRESS,
|
38 |
function handleProgress (event:ProgressEvent):void
|
39 |
{
|
40 |
var percent:Number = Math.round(event.bytesLoaded / event.bytesTotal * 100);
|
41 |
txt.text = percent + "%";
|
42 |
txt.setTextFormat(txtFormat);
|
43 |
shape.graphics.lineStyle(_lineH-1, 0x888888, 10);
|
44 |
shape.graphics.moveTo(1, _lineH/2);
|
45 |
shape.graphics.lineTo(percent*_lineLen/100, _lineH/2);
|
46 |
});
|
47 |
this.loaderInfo.addEventListener(Event.COMPLETE, function()
|
48 |
{
|
49 |
play();
|
50 |
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
|
51 |
});
|
52 |
}
|
53 |
|
54 |
private function enterFrameHandler(event:Event):void {
|
55 |
if (currentFrame >= 3) {
|
56 |
this.removeChild(loadInfo);
|
57 |
loadInfo = null;
|
58 |
removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
|
59 |
stop();
|
60 |
}
|
61 |
}
|
62 |
}
|
63 |
} |
另外,我也用直接拖放 MovieClip 的方法修改了 Den 的例子程序,也是完全正常的,这里也提供
到这里,虽然仍然比 AS2 中多了一些步骤,毕竟算是解决了这个问题,一个如此基本的需求,被 ADOBE 搞的这么难,真该好好反省反省!
====================================================================
我是一个.NET的程序员,起先由于公司的需要,不得不学习Action Script。当时情况很糟,项目来了,迫在眉睫,但是公司舍不得出钱请个AS的程序员(AS的程序员要价总是太高),我只能一边学习,一边做项目,幸好Action Script 3.0的面向对象的,这让我感觉也不太难懂,经过一段时间学习,慢慢对于Flash的理解要稍稍多了一些,不知不觉,公司又接了几个Flash的项目,感觉都还能胜任。不过,直到昨天,我才明白Flash的loading的含义。
以下言论是我自身的理解,不保证100%正确,如有谬误,还请指出,并见谅。
你是否曾经被要求把Flash做到加载几KB就要显示呢,如果有,并且你不知道如何解决的话,请继续向下看;如果您是高手,并且也有兴趣的话,也请继续,并在适当的时候指出我的我的错误。
Flash要显示内容,至少得等第一桢下载完毕,很简单吧。
不过,你是否清楚你的第一桢有多大呢?如果你说你的第一桢很小,因为你留的是一个空白的桢,那不一定。现在我的这个项目的Flash里,做了很多的linkage,您可能明白了,Action Script导入的那一桢不管是不是空白,都是可以很大,所以,在我们经典论坛的Flash版块里总是会看到版主说,做loading用三桢,第一桢loading,第二桢导出脚本,第三桢开始程序……
http://bbs.blueidea.com/thread-2853462-1-1.html
但是为什么要这样做呢?
您还记得Flash至少要加载第一桢才能显示的道理吧,所以,要尽快的显示Flash的loading,要尽量减小第一桢的大小,把loading的逻辑写在第一桢,将Action Script导出在第二桢,这样,导出的内容就影响不到你,最后,你会在第三桢,也就是加载完成后,开始主程序逻辑。
在Flash的publish settings(快捷键Ctrl+Shift+F12),选择"Flash"选项卡,点击"Script"的那个"settings",把"Export class in frame 1"改成"Export class in frame 2",这样,你的程序的类会导出到第二桢,它不会把第一桢(loading所在的桢)变大,从而进快显示loading。
看看效果吧,在publish settings中,把"Generate size report"勾选,看看发布后flash文件的体积报告:
Frame # Frame Bytes Total Bytes Scene
1 23892 23892 Main Scene
2 155519 179411 (AS 3.0 Classes Export Frame)
3 36 179447
而把Action Script发布到第一桢呢:
Frame # Frame Bytes Total Bytes Scene
1 106125 106125 Main Scene (AS 3.0 Classes Export Frame)
2 70771 176896
3 36 176932
你看,如果导出到第二桢,第一桢的体积大约是24KB,这是因为loading动画效果有这样大的体积;而如果将AS发布到第一桢,则变成了106KB,主要的加载内容都在第一桢了,这说明这个loading就比较失败。
另外,你可能还会遇到这样的问题:当你辛苦的为你的loading写了一个类,叫myFlashAppPreLoader,在第一桢时,你是这样写的:
//private variables
private var loader:myFlashAppPreLoader;
……
//constructor
loader=new myFlashAppPreLoader();
this.addChild(loader);
this.loaderInfo.addEventListener(Event.COMPLETE,loadCompetedHandler);
this.loaderInfo.addEventListener(ProgressEvent.PROGRESS,LoadingHandler);
……
//some other function
private function loadCompetedHandler(e:Event) {
this.removeChild(loader);
}
你这样做的原因在于,你想把loading封装成一个类,你直接用就好了,方便!
可是,这样带来一个极大的问题:
试想,你刚刚把AS导出在第二桢了,现在你的第一桢是loading,第一桢就用第二桢的东西……逻辑不太对吧?
所以,在第一桢的loading里,不要把loading写成类,请直接在舞台的第一桢里放loading的动画,对,把它们通通放到舞台上,这样,你可以直接在document class中对其中进行操作,想要什么动画也可以自行控制了。
//constructor
this.loaderInfo.addEventListener(ProgressEvent.PROGRESS,LoadingHandler);
……
//loading animation handler
private function LoadingHandler(e:ProgressEvent):void {
txtProgress.text=String(Math.round(e.bytesLoaded/e.bytesTotal*100));
}
这里的txtProgress就是舞台上一个DynamicTextField的实例名称,我直接用它赋一个百分比的值,如果你loading的时候要控制更多的元件,也可以同样这样做。
末尾的时候,提醒大家多留意一个size
------------------------------------