35首页的Banner印象中是05还是06年写的忘了。当时改过几个版本,有时间控制的,有外部SWF触发,直到后来的帧控制,可谓是经过了不少的“磨难”,从最早的AS到后来的AS2.0,再到现在的AS3.0。
在构思第一版本的时候,中途研究了不少别人写的Flash图片轮播系统,了解其工作原理,所以,最早的版本是根据图片轮播的思路来,由机器生成的时钟点来切换SWF,即通过setInterval来实现,后来的使用过程中发现问题很多,比如网速、内部SWF播放时长等,往往动画还没播完就被强制切换了,最终放弃了这个版本,于是改成外部SWF来触发。
这种做法后来证实挺好用的,唯一不足的是,在做外部SWF的时候需要在最后一帧添加跳转的代码,控制主容器的动作。而由子项去控制父容器的操作,往往都是不受欢迎的,因为不够灵活。而且,当外部动画忘记添加跳转的动作时(比如动画制作人员变更),或者需要加载站外的SWF时,那问题就又出现了,风险还是挺大的,于是决定转换思路,将主容器的控制由被动转主动,监听外部SWF的播放情况来控制内容切换,到目前为止,这种思路还未发现问题,所以,此次的AS3.0的改版就是基于这个思路之上。
讲了这么多版本的历史及优缺点,现在咱来讲讲AS3.0的代码结构,俺们先来看张简图:

整个调用的处理都是由AS3.0提供的监听事件来完成,传闻AS3.0的监听机制很牛X,目前为止仅使用到现阶段,不好说什么。
为了方便下文,再来看看被载入的数据结构:

首先,为了让整个数据处理得更顺畅些,在原始数据刚被载入的时候就进行相应的预处理,进行数据筛选分组,以便使所有的数据看起来更有序。在载入数据的时候,AS3.0提供了比以往版本更加灵活的方法,如果被载入的数据是结构完整的XML数据,则其会直接将XML的节点当做XML对象的属性来使用,大大简化了XML的操作方法。
仅仅两句代码就结束了XML数据的读取:
1 2
| var xml:XML = new XML(xmlLoader.data); var node:XMLList = xml.child("banner"); //这个banner就是XML的节点
|
预处理的方法如下:
1 2 3 4 5 6 7 8
| for(var n = 0, len = node.length(); n < len; n++){ xmldata.push({ path : node.path[n], txt: node.txt[n], link : node.link[n], target : node.target[n] }); }
|
其中,为了方便其他方法能顺畅地调用这个处理后的数据,这里使用全局的数组对象xmldata。
再根据这个处理后的数据集,生成供用户随时切换的按钮集合:
1 2 3 4 5 6 7 8 9 10 11 12 13
| for(var s = 0, nodes = xmldata.length; s < nodes; s++){ var index:int = s + 1; var btn_mc:MovieClip = new btn(); //库元素中创建的影片剪辑,有两帧,分别表示按钮的两种状态。 btn_mc.buttonMode = true; btn_mc.current = s; CreateNumText(index, btn_mc); //该方法为动态创建标签编号,使用TextField的方法实现。 btn_mc.addEventListener(MouseEvent.CLICK, AttachMouseClick); //为按钮添加监听点击的动作 btn_mc.x = 25 * index; btn_mc.y = screen.stage_height - 25; btn_collection.push(btn_mc); //为了方便其他方法对按钮的状态进行处理,将创建好的按钮存放到数组中。 removeEventListener(MouseEvent.CLICK, arguments.callee); //移除监听 addChild(btn_mc); //将新实例出来的按钮添加到主场景中 }
|
然后加载外部动画,代码结构看起来好像有点山寨,但逻辑我想应当很容易看出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| //创建载入动作条,在载入较大的动画时不至于让用户感觉浏览器当机 loadBar.graphics.beginFill(0xff0000); loadBar.graphics.drawRect(0,screen.stage_height - 2,1,2); loadBar.graphics.endFill(); addChild(loadBar); //获取场景中播放动画使用的容器,由于目前场景只有一个播放的容器MC,所以直接使用getChildAt(0)来转换。 var mc:MovieClip = getChildAt(0) as MovieClip; var path:String = xmldata[index].path; //从预处理后的数据集中调取需要用到的数据,这里的index为播放索引 var loader:AVM2Loader = new AVM2Loader(); //使用动态重写Loader方法,由libspark.org(1)提供。 //var loader:Loader = new Loader(); loader.load(new URLRequest(path)); //添加各类监听操作,包括即时跟踪外部动画的大小,载入情况等。 loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, LoadProgress); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(evt:Event){ mc.addChild(loader); var new_mc:MovieClip = MovieClip(evt.currentTarget.loader.content); current_mc = new_mc; var url:String = xmldata[index].link; var win:String = xmldata[index].target; if(url != ""){ //当Link项不为空的时候,为影片剪辑添加鼠标点击动作。 if(win == ""){ win = "_self"; } new_mc.buttonMode = true; new_mc.addEventListener(MouseEvent.CLICK, function(){ navigateToURL(new URLRequest(xmldata[index].link), win); }); new_mc.removeEventListener(MouseEvent.CLICK, arguments.callee); //移除鼠标监听动作 } new_mc.addEventListener(Event.ENTER_FRAME,EnterFrame); //监听外部SWF的播放情况 removeChild(loadBar); loader.removeEventListener(ProgressEvent.PROGRESS, arguments.callee); loader.removeEventListener(Event.COMPLETE, arguments.callee); });
|
与载入外部动画有关的相关方法:
1 2 3 4 5 6 7
| //监听外部SWF的载入情况 private function LoadProgress(evt:ProgressEvent){ var loaded:int = evt.bytesLoaded; var total:int = evt.bytesTotal; var scaleWidth:int = (loaded * screen.stage_width) / total; loadBar.scaleX = scaleWidth; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| //监听外部SWF的播放情况 private function EnterFrame(evt:Event):void{ var mc:MovieClip = evt.target as MovieClip; trace("thread" + test_index + ": " + mc.currentFrame + "total: " + mc.totalFrames); if(mc.currentFrame == mc.totalFrames){ s_index ++; if(s_index >= btn_collection.length){ s_index = 0; } LoadNewSwf(s_index); mc.removeEventListener(Event.ENTER_FRAME, arguments.callee); //播放完成之后移除监听 } }
|
整个Banner的架构大致就是这样的,当然,在编写的过程中,相关的方法注意引用进来,以免造成方法或对象无法使用的问题,这里我使用了以下包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import flash.display.Sprite; import flash.display.Stage; import flash.display.Loader; import flash.display.Shape; import flash.display.Graphics; import flash.display.MovieClip; import flash.display.LoaderInfo; import flash.xml.*; import flash.net.URLLoader; import flash.net.URLRequest; import flash.net.navigateToURL; import flash.text.TextField; import flash.utils.*; import flash.events.*; import com.ubbcn.*; //自制包,包含AVM2Loader的方法及其他必要对象。
|
在编码的过程中碰到不少问题,其中,最大的问题应当就是不同版本的SWF被AS3.0加载进来时制式(2)转换的问题了,AS3.0的制式是AVM2,而之前的版本均是AVM1,很令人抓狂。就这个问题当时处理了好几天也没搞定。当然,现阶段使用的是Hack的方法大致处理好了,但就我个人看来还是没有完美解决,问了不少人,都说这个暂时没办法,版本限制死了,只能在制作外部SWF时注意一下发布的版本。
注(1):AVM2Loader方法是使用别人Hack的方法,具体的代码及使用方法可以移步:http://www.libspark.org/wiki/fladdict/AVM2Loader,这里先感谢一下这些不知名的作者们。
注(2):制式这个词不知道这里使用合不合适,暂时没有找到更贴切的描述。
PS:此篇文章应部分网友要求特别编写,如需转载请注明转自ui.35.com。
好东西我们会尽可能的和大家一起分享,同时也请转载时尊重下我们的劳动付出,谢谢~~!
Juns 2009-10-13 1:22 pm 说:
强大的35.comBanner终于解密了!!
[回复]
SOS 2009-10-13 1:25 pm 说:
[回复]
灯心草 2009-10-14 5:24 pm 说:
呃。。研究研究。。
[回复]
撒啊分 2009-10-16 6:30 pm 回复:
还行
[回复]