听蛙居 – 帅青蛙的工作与生活

林莺啼到无声处,青草池塘独听蛙……

35首页Banner框架解密

35首页的Banner印象中是05还是06年写的忘了。当时改过几个版本,有时间控制的,有外部SWF触发,直到后来的帧控制,可谓是经过了不少的“磨难”,从最早的AS到后来的AS2.0,再到现在的AS3.0。

回想当年接到这个构思的时候人就郁闷了,因为一方面没怎么接触过Flash,二是里面调用的是动画,而不是普通的图片;不过最后还是硬咬着牙给挺过来了。

在构思第一版本的时候,中途研究了不少别人写的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
//创建载入动作条,在载入较大的动画时不至于让用户感觉浏览器当机
loadBar.graphics.beginFill(0xff0000);
loadBar.graphics.drawRect(0,screen.stage_height  - 2,1,2);
loadBar.graphics.endFill();
addChild(loadBar);
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
//获取场景中播放动画使用的容器,由于目前场景只有一个播放的容器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):制式这个词不知道这里使用合不合适,暂时没有找到更贴切的描述。

帅青蛙
2009年11月04日

关键字:, ,


本博客内容大部分属于原创,若需转载请注明出处。商业用途请与我联系

还没有相关批示……

我来批示

1531811292045171967128131016114

随机显示的5篇日志

批示最多的5篇日志

©2009 听蛙居 – 帅青蛙的工作与生活版权所有
Powered by Wordpress. 感谢huangna提供空间 & 感谢Mojojo提供设计。