本实例很简单,一级导航由于宽度是固定了的,所以不做成动态输出了,不过在xml中还是会做相关的配置(比如名称、链接等)。二级的导航就是通过读取xml中的内容类加载和显示的。先预览一下效果:点击
需要说明一点,按道理来说,点击某个导航项之后应该标示出当前所在的导航项(当前页面),这里没有实现这个需求,是因为导航是嵌入到网页文件里面的,当单击导航之后会发生跳转,为了在新打开的页面中标示出导航当前所在的导航项,我们给swf文件传值即可,所以没有必要再当前页面标示出当前所在项(跳转之后新页面通过传值的方式设定当前所在项)。不知道说明白没有,不明白先往下看应该能明白。。。
一、先配置好xml文件
保存为site-nav.xml。结构如下:
<?xml version="1.0" encoding="utf-8"?> <site_nav> <top_nav name="首页" link="http://www.google.com" coo=""></top_nav> <top_nav name="优秀教程" link="http://www.google.com" coo="160"> <sub_nav link="http://www.google.com">子导航一</sub_nav> ...... <sub_nav link="http://www.google.com">子导航七啊啊</sub_nav> </top_nav> <top_nav name="优秀教程" link="http://www.google.com" coo="160"> <sub_nav link="http://www.google.com">子导航一</sub_nav> ...... <sub_nav link="http://www.google.com">子导航七啊啊</sub_nav> </top_nav> </site_nav>
我没有把所有菜单项都列出来,因为有点长,比较占版面,知道xml的结构就可以了。
coo这个属性的作用是设置每个二级菜单出现的位置。首页没有二级菜单,所以没有sub_nav节点。
二、制作一级导航。
前面说了由于一级导航固定不变,所以不做动态输出了,所以我们直接在flash软件里面制作出来。一级导航只有两种状态,当鼠标移开时文字变灰,移到其上面时文字变白,我们制作好一个,其他个直接复制元件就可以了。
1、先用矩形工具根据每个菜单项的宽度和高度画一个矩形,然后转换成影片剪辑,注意,元件注册点为正中心,比如:

2、双击进本影片剪辑内部,新建一个图层,用文字工具打入菜单项文字,比如这里“首页”:

3、按F6插入一个关键帧,然后把文字的颜色改成白色的(鼠标滑过的时候改变成白色):
,时间轴上的帧应该是这样的:
,第一帧记得加上“stop()”代码。
然后把背景的那个矩形的颜色的透明底改成0,这个透明底只是为了设置鼠标响应范围而画的。
4、一个菜单项制作好之后,其他的复制元件,改下文字就好了。先选择刚刚制作好的菜单项,ctrl+c、ctrl+v复制一个,然后在复制出来的菜单项上右击,在出来的右键菜单中选择“直接复制元件…”,弹出的对话框中选确定,然后双击进入这个复制出来的菜单项,双击文本进行修改。之后用同样的方法复制和制作剩余的菜单项,放到响应的位置,如下:

5、给每个菜单项命名,依次命名为nav0,nav1,…。

6、制作鼠标经过时那个跟随的红色块,你可以画出来,我是从平面里面截图的,新建一个图层,把图片拖入舞台,然后转换为影片剪辑。之后给其命名为:hover_mc

三、创建文档类MainNav.as
代码我实在Flash Develop里面写的,目前我们先不考虑二级导航的情况。代码如下:
package AS {
//导入必要的类
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.navigateToURL;
import flash.net.URLRequest;
import flash.net.URLLoader;
import com.greensock.TweenLite;
import flash.display.StageScaleMode;
//下面两个是自定义的缓动函数用的。
import fl.motion.BezierSegment;
import flash.geom.Point;
public class MainNav extends MovieClip {
//定义相关属性
//_currentTag为当前一级菜单所在的菜单项
private var _currentTag:int;
//_subTag为二级菜单所在的项
private var _subTag:int;
//_overTag的定义主要是为了重置上个鼠标滑过时菜单项的状态的,滑过时走到第二帧,划开时又回到第一帧。
private var _overTag:int = _currentTag;
//xml所在路径
private var _xmlPath:String = "site-nav.xml";
//xml数据本身
private var _navData:XML;
//缓动的时间
private var _easeDelay:Number = 2;
public function MainNav():void {
if (stage) {
init();
}else {
addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init(e:Event=null):void {
stage.scaleMode = StageScaleMode.NO_SCALE;
//先加载本身,加载完成之后再加载xml文件
loaderInfo.addEventListener(Event.COMPLETE, mainLoaded);
}
private function mainLoaded(e:Event):void {
loaderInfo.removeEventListener(Event.COMPLETE, mainLoaded);
//加载xml
var xmlLoader:URLLoader = new URLLoader();
xmlLoader.load(new URLRequest(_xmlPath));
xmlLoader.addEventListener(Event.COMPLETE, xmlLoaded);
//下面这个临时变量,主要是获取网页里面传递过来的值,网页里面传递方式形如:index-nav.swf?n=20
//比如:n=20表示第三个一级导航项下面的第一个二级导航,n=33表示第四个一级导航下的第四个二级导航。
//单击导航页面跳转之后导航的状态就是由这个值了决定的。
var tempVar:int;
tempVar = int(loaderInfo.parameters["n"]);
//tempVar = 63;
if (tempVar > 0) {
//对传递过来的n进行拆分,十位数表示一级导航状态,个位数表示二级导航状态
var str:String = String(tempVar);
_currentTag = int(str.substr(0, 1));
_overTag = _currentTag;
_subTag = int(str.substr(1, 1));
}
}
private function xmlLoaded(e:Event):void {
//保存加载到的xml数据到_navData变量
_navData = XML(e.target.data);
//循环初始化每个一级导航项的相关设置
for (var i = 0; i < 8; i++) {
//动态添加一个自定义属性
this["nav" + i].id = i;
//按钮模式,这样就出现手型
this["nav" + i].buttonMode = true;
//孩子不接收鼠标事件
this["nav" + i].mouseChildren = false;
//添加鼠标滑过监听器,注意ROLL_OVER事件不参与冒泡
this["nav" + i].addEventListener(MouseEvent.ROLL_OVER, mainNavOver);
//添加单击事件侦听器
this["nav" + i].addEventListener(MouseEvent.CLICK, mainNavClick);
}
//侦听鼠标移除舞台(导航)时的事件,好重置菜单状态
this.addEventListener(MouseEvent.ROLL_OUT, outStage);
//设置默认的状态
this.hover_mc.x = this["nav" + _currentTag].x;
this["nav" + _currentTag].gotoAndStop(2);
}
private function outStage(e:MouseEvent):void {
//_overTag变量表示上次鼠标所在哪个菜单项上面,不等于_currentTag变量,则需要重置导航的状态
if (_overTag != _currentTag) {
//鼠标所在的菜单项跳转到第一帧(文字变灰)
this["nav" + _overTag].gotoAndStop(1);
//当前所选中的菜单项跳转到第二帧(文字变白)
this["nav" + _currentTag].gotoAndStop(2);
//色块缓动到当前所在菜单项下
TweenLite.to(this.hover_mc, _easeDelay, { x:this["nav" + _currentTag].x, ease:ease } );
//重置_overTag的值。
_overTag = _currentTag;
}
}
private function mainNavOver(e:MouseEvent):void {
//鼠标在某个菜单项上时,这个菜单项跳转到第二帧
e.target.gotoAndStop(2);
if (_overTag != e.target.id) {
//上次鼠标所在的菜单项不是本菜单项时,上个菜单项回到第一帧(文字变灰)
this["nav" + _overTag].gotoAndStop(1);
}
//红色块跟随鼠标,滑到响应的菜单项下
TweenLite.to(this.hover_mc, _easeDelay, { x:e.target.x, ease:ease } );
//设置_overTag为当前的菜单项id
_overTag = e.target.id;
}
private function mainNavClick(e:MouseEvent):void {
if (_currentTag != e.target.id) {
//当前菜单项和所点击的菜单项不同时,重置当前菜单项
_currentTag = e.target.id;
//跳转到响应的链接
//navigateToURL(new URLRequest(_navData.top_nav[_currentTag].@link),"_top");
trace(_navData.top_nav[_currentTag].@link);
}
}
//下面这个函数是一个自定义的缓动函数,是用Easing Generator生成的,需要可以找我
private function ease(t:Number, b:Number, c:Number, d:Number):Number{
var points:Array = [
{point:[0,0],pre:[0,0],post:[0,0.59]},
{point:[0.37,1.04],pre:[0.13,1.05],post:[0.52,1.03]},
{point:[1,1],pre:[0.68,0.92],post:[1,1]},
];
var bezier:BezierSegment = null;
for (var i:int = 0; i < points.length - 1; i++) {
if (t / d >= points[i].point[0] && t / d <= points[i+1].point[0]) {
bezier = new BezierSegment(
new Point(points[i].point[0], points[i].point[1]),
new Point(points[i].post[0], points[i].post[1]),
new Point(points[i+1].pre[0], points[i+1].pre[1]),
new Point(points[i+1].point[0], points[i+1].point[1]));
break;
}
}
return c * bezier.getYForX(t / d) + b;
}
}
}
在flash环境中绑定一下这个文档类,然后就可以测试了,大致看出效果,只是还没加入二级菜单的制作。
四、加入二级菜单。
每个二级菜单项都是根据xml文件里面的设置动态生成的,他们的状态、鼠标滑过划开效果等都一样,所以我们考虑用单独的一个类来写,动态生成的时候生成这个类的实例就可以了。首先来看一下这个类吧:
package AS {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import com.greensock.TweenLite;
import com.greensock.easing.Expo;
import flash.net.navigateToURL;
import flash.net.URLRequest;
public class SubNavItem extends Sprite {
//导航项的名称
private var _navName:String;
//导航项的id
private var _navNum:int;
//需要跳转到的链接
private var _toUrl:String;
//是否选中状态,选中则又灰色背景,没有选中则没有底
private var _isTag:Boolean = false;
//底色块
private var _bg:Sprite=new Sprite();
//通过构造函数传入相关的值
public function SubNavItem(name:String = "子菜单项", num:int = 0,toUrl:String="http://www.google.com/",isTag:Boolean=false):void {
super();
_navName = name;
_navNum = num;
_toUrl = toUrl;
_isTag = isTag;
//创建动态文本域
var t:TextField = new TextField();
//根据字符数来决定文本域的宽度
t.width = _navName.length * 15>=60?_navName.length * 15:60;
t.textColor = 0x959595;
t.selectable = false;
t.text = _navName;
//文本居中对齐
var tf:TextFormat = new TextFormat();
tf.align = TextFormatAlign.CENTER;
t.setTextFormat(tf);
//根据文本域的宽画一个矩形灰底
_bg.graphics.lineStyle();
_bg.graphics.beginFill(0xdcdcdc);
_bg.graphics.drawRect(0, 1, t.width, 18);
_bg.graphics.endFill();
//添加进显示列表
this.addChild(_bg);
this.addChild(t);
//孩子不接受鼠标响应
this.mouseChildren = false;
this.buttonMode = true;
if (!_isTag) {
//如果不是选中状态的,则不显示背景
_bg.width = 1;
_bg.alpha = 0;
//添加鼠标滑过和滑出监听器
this.addEventListener(MouseEvent.ROLL_OVER, overSubItem);
this.addEventListener(MouseEvent.ROLL_OUT, outSubItem);
}
//添加点击事件监听器
this.addEventListener(MouseEvent.CLICK, clickSubItem);
}
private function clickSubItem(e:MouseEvent):void {
//单击发生,通过navgateToURL直接跳转到响应的页面
trace(this._toUrl);
}
private function overSubItem(e:MouseEvent):void {
//鼠标滑过时,背景缓动出来
_bg.alpha = 1;
TweenLite.to(this._bg, .6, { width:this.width, ease:Expo.easeOut } );
}
private function outSubItem(e:MouseEvent):void {
//滑开时,背景缓动收起,收完之后设置透明度为0,使其不可见
TweenLite.to(this._bg, .6, { width:1, onComplete:setAlpha, ease:Expo.easeOut } );
}
//下面要不要没什么关系,如果不考虑获取和存取属性的值
private function setAlpha():void {
_bg.alpha = 0;
}
public function get navName():String {
return _navName;
}
public function set navName(str:String):void {
_navName = str;
}
public function get navNum():int {
return _navNum;
}
public function set navNum(n:int):void {
_navNum=n;
}
public function get toUrl():String {
return _toUrl;
}
public function set toUrl(str:String):void {
_toUrl = str;
}
public function get isTag():Boolean {
return _isTag;
}
public function set isTag(b:Boolean):void {
_isTag = b;
}
}
}
五、修改文档类,生成二级导航项。
1、首先,我们定义一个数组来保存每个一级导航下面的二级导航(导航项的集合),定义到属性里面:
//定义一个数组来保存每个二级菜单(是菜单项的集合,每个一级导航对应一个二级导航) private var _subItemsArr:Array = [];
2、修改xmlLoaded()函数,添加如下的代码:
//定义两个临时变量,用于循环内设置每个菜单项的位置
var _count:Number;
var oldWidth:Number;
//外层循环,循环xml数据中每个top_nav节点
for (var j:int = 0; j < _navData.top_nav.length(); j++) {
_subItemsArr[j] = new Sprite();
//x左边为1000,意思是先不要在舞台上显示
_subItemsArr[j].x = 1000;
_subItemsArr[j].y = 39;
//内层循环,循环每个sub_nav节点
for (var m:int = 0; m < _navData.top_nav[j].sub_nav.length(); m++) {
//创建二级菜单项的实例,把相关的值通过构造函数传递过去
var tempSubItem:SubNavItem = new SubNavItem(_navData.top_nav[j].sub_nav[m].toString(), j * 0 + m, _navData.top_nav[j].sub_nav[m].@link, j==_currentTag&&_subTag==m?true:false);
if (m > 0) {
//每个菜单项之间相隔5像素距离,通过_count的累加设置菜单项的x坐标,使每个菜单项依次排开
_count = _count + oldWidth+5;
}
//保存当前这个菜单项的宽度,以在下一次循环中累加
oldWidth = tempSubItem.width;
//设置当前菜单项的x坐标
tempSubItem.x = _count;
//添加进我们上面定义的数组里面,方便后面的操作
_subItemsArr[j].addChild(tempSubItem);
}
//重置_count
_count = 0;
//把这些菜单添加进显示列表
this.addChild(_subItemsArr[j]);
}
//根据_currentTag的值设置当前菜单项的x坐标,坐标的值就是我们xml中设置的coo属性的值
_subItemsArr[_currentTag].x= _navData.top_nav[_currentTag].@coo;
3、修改outStage()函数,鼠标移出舞台时,还需重置二级菜单的位置,加入如下代码:
//重置二级菜单的显示与隐藏,通过控制其x坐标达到效果 _subItemsArr[_overTag].x = 1000; _subItemsArr[_currentTag].x = _navData.top_nav[_currentTag].@coo;
4、修改mainNavOver()函数,鼠标滑过各个一级菜单项时,还需控制二级菜单的显示和隐藏:
private function mainNavOver(e:MouseEvent):void {
//鼠标在某个菜单项上时,这个菜单项跳转到第二帧
e.target.gotoAndStop(2);
//上个二级菜单移到舞台外隐藏起来
_subItemsArr[_overTag].x = 1000;
if (_overTag != e.target.id) {
//上次鼠标所在的菜单项不是本菜单项时,上个菜单项回到第一帧(文字变灰)
this["nav" + _overTag].gotoAndStop(1);
}
//红色块跟随鼠标,滑到响应的菜单项下
TweenLite.to(this.hover_mc, _easeDelay, { x:e.target.x, ease:ease } );
//设置_overTag为当前的菜单项id
_overTag = e.target.id;
//当前鼠标所在一级导航下的二级导航显示出来
_subItemsArr[_overTag].x = _navData.top_nav[_overTag].@coo;
}
到此就完成了本实例的制作,测试看看效果吧。^_^








