真正开始研究Flash Preloader,还是因为最近研究起PV3d。用PV3d的时候,问题一下子就暴露了。

如果是以往的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帧导出:

  1. 如果main time line 使用了类,那么类就会在第1帧被导出。这里的“使用”包含创建类的对象、调用类的静态方法、使用类赋值给其它变量等,还包含使用类声明变量的类型,但不包含import类。
  2. 如果在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

RSL与自载 摘文

        如上图所示,我在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 类了如指掌,那么你能实现的功能远不止这些。

----------------------------------------------------------

 

  Flash 编程中,如果要在 AS2 中加一个自加载进度条,已经有了很多成熟的方法,我找到并且使用的是一段纯代码,这段代码会去创建一个 MovieClip,然后在上面用作图的方式显示进度,当然,还需要把所有的选择了“Export for ActionScript”的 Symbol 中的 “Export in first frame”取消,然后在 Settings 中的“ActionScript Settings”把“Export classes in frame”设置为 2 就可以了。

  但是,就是这么一个基本的需求,在 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 给出了一个RSL与自载 摘文例子程序相关文章,文章貌似俄文的,例子程序倒是确实可以正确运行的,呼呼,终于找到了。

  回复中,Den Ivanov 还提到了它的实现原理:
in short

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 的例子程序,也是完全正常的,这里也提供RSL与自载 摘文我修改过的这个例子程序

  到这里,虽然仍然比 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

 

------------------------------------

相关文章:

  • 2021-10-01
  • 2022-12-23
  • 2022-12-23
  • 2022-01-15
  • 2021-06-07
  • 2021-07-14
  • 2021-06-26
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2021-05-21
  • 2021-09-25
  • 2022-12-23
  • 2021-09-28
  • 2021-12-06
  • 2021-12-18
相关资源
相似解决方案