论坛交流
首页办公自动化| 网页制作| 平面设计| 动画制作| 数据库开发| 程序设计| 全部视频教程
应用视频: Windows | Word2007 | Excel2007 | PowerPoint2007 | Dreamweaver 8 | Fireworks 8 | Flash 8 | Photoshop cs | CorelDraw 12
编程视频: C语言视频教程 | HTML | Div+Css布局 | Javascript | Access数据库 | Asp | Sql Server数据库Asp.net  | Flash AS
当前位置 > 文字教程 > Flash AS编程教程
Tag:2.0,3.0菜鸟,游戏,,cs,技巧,源码,,文本,文字,函数,音乐,随机,拖拽,asp,access,xml,mc,视频教程

用FLASH制作超酷逼真的MP3播放器

文章类别:Flash AS编程 | 发表日期:2008-10-6 17:36:37


图1 播放器界面
图2 设置界面
1.功能简介
2.结构与符号组成
3.程序控制剖析
4.歌曲列表问题
5.致谢
 
1.功能简介
本播放器在FalshMX中制作,WinXP平台下运行正常。外型见图1:播放器界面 及图2:设置界面
本播放器充分利用了Flash软件内置的几个组件及通用按钮,完成对MP3播放控制。尤其特别的地方在于,利用组件来控制播放方式、播放次数、中英文显示等内容;利用播放面板上的滑块还可调节文字的跑动速度。
歌曲列表采用XML文件形式,方便Flash调用和用户修改。此外,要想使播放器正常使用,需将XML文件和SWF文件以及MP3文件夹放在同一个目录下。比如笔者机器中的播放器调用的MP3文件位于backstreet boys文件夹中,SWF文件及XML文件和这个文件夹都位于D:\Music下。
此播放器唯一的Bug是歌曲播放时间显示问题,如果用户在播放过程中点击按钮或进行设置,就会造成歌曲播放与其长度显示不匹配问题。本人想尽办法也无法解决,还请高手指教。
 
2.结构与符号组成 
播放器源文件的层结构见图3:
从上到下依次存放的是:
Actions层只存放主控程序;
Preference层存放设置面板、MainControll层存放所有的控制按钮;
Borders层存放界面立体效果所需的线条、SkinsAndInnerTextMasks层存放界面色块和两个用于遮盖跑动文字的小方框(两个小方框颜色和界面色块相同);
OuterTextMask层存放两个矩形用于遮盖跑到外面的文字,其颜色和舞台背景色相同;TextDisplay层存放滚动的文字。
播放器所用的Symbols库见图4:
Knobs&Faders符号包中放置的是两个经过修改的通用滑杆按钮;
CountDown符号用于倒记时显示歌曲总长度,场景中的CountDown符号上面有Actions;
En_Cn_display符号包放置的是显示英文和中文所需的符号,其中的嵌套关系是chDisp_mc包含ctext_mc,graphMask电影片段用于决定是否遮滚动文字,初始状态不遮盖;
playControll符号包存放播放控制按钮;
preference符号包存放设置播放及显示所用的flash组件;
rhythmbar符号包存放音频变化柱形图,可惜的是无法真实表现音频规律只是个装饰而已;
skins符号是界面图案;
text_mask符号用于遮盖跑到外面的文字,否则看起来很难看,我试过用遮罩但不行。
图3 层结构
图4 符号库
 
3. 程序控制剖析
播放器再制作过程中碰到的主要困难有程序控制问题比如主程序与组件的配合,其次是对滑杆按钮的修改及滑杆按钮对滚动文字和音量大小的控制,最后是时间显示问题。
3.1 舞上Action层中的内容台
//////////////////////////////////////////////////////////
Frame1上的主控程序源代码及解释
//////////////////////////////////////////////////////////


//定义XML文件载入后状态的函数,如果解析成功告知用户finished loading the XML document.
//否则告诉用户there was an error loading the XML document.
function myOnLoad(success) {
  if (success) {
    trace("finished loading the XML document.\n");
    //将XML文件的根节点传递给musicParser函数
    musicParser(xmlObject.firstChild);
    unplayStatus();
  } else {
    trace("there was an error loading the XML document.\n");
  }
}
//定义解析XML文件所用的函数
function musicParser(branch) {
  mySongs = new Array();
  songList = branch.firstChild;
  for (i=0; i<branch.childNodes.length; i++) {
    source = songList.firstChild.firstChild.nodeValue;
    series = i+1;
    eshow = songList.firstChild.nextSibling.firstChild.nodeValue;
    cshow = songList.lastChild.firstChild.nodeValue;
    mySongs[i] = {file:source, order:series, english:eshow, chinese:cshow};
    songList = songList.nextSibling;
  }
}
//定义曲目播放所需的初始变量
function unplayStatus() {
//歌曲总数songAmount从XML文件中获得
   songAmount = xmlObject.firstChild.childNodes.length;
   curentsong = 0+"/"+(songAmount-1);
   //默认播放第一首歌
   activeTrack = 0;
   addtime = 0;
   //默认初始状态为不播放
   begin = false;
}
//定义用于容纳歌曲列表文件music.xml中歌曲信息的XML对象
xmlObject = new XML();
xmlObject.ignoreWhite = true;
//如果xmlObject对象加载music.xml文件成功,返回给myOnload函数"true",否则返回"false"
xmlObject.onLoad = myOnLoad;
xmlObject.load("music.xml");

/////////////////////////////////////////////////////////////////
//设定界面不允许缩放和右键菜单
fscommand("allowscale", "false");
fscommand("showmenu", "false");
//////////////////////////////////////////////////////////////////

设定包含文字的文本框可随内容大小伸缩
enDisplay.t1.show1.autoSize = true;
chDisplay.t2.show2.autoSize = true;

/////////////////////////////////////////////////////////////////
//转换带小数点的秒数sec为标准的00:00格式
function FormatTime(sec) {
  Minutes = Math.floor(sec/60);
  if (Number(Minutes)<10) {
    Minutes = "0"+Minutes;
  }
  Seconds = int(sec-(Minutes*60));
  if (Number(Seconds)<10) {
    Seconds = "0"+Seconds;
  }
  return (Minutes+":"+Seconds);
}
////////////////////////////////////////////////////////////////
// 定义点击播放按钮时激发的函数,需要的唯一参数是播放次数
playTrack = function (num) {
  var p = this;
  //默认的播放次数time为1
  var time = 1;
  //每次点击播放按钮都要删掉原来的声音对象,防止占用内存
  delete s;
  //新建一个声音对象
  s = new Sound();
  //如果随机播放设置为真,则播放随机数对应歌曲
  if (_root.pref.rplay.getState()) {
    activeTrack = random(songAmount-1);
  } else {
    trace(activeTrack);
  }
  //载入第一首歌曲并播放
  s.loadSound(mySongs[activeTrack].file, true);
  //显示歌曲对应的英文和中文名字
  _root.enDisplay.t1.show1.text = mySongs[activeTrack].english;
  _root.chDisplay.t2.show2.text = mySongs[activeTrack].chinese;
  //在主面板上的当前曲目显示当前曲目顺序及曲目总数
  curentsong = (mySongs[activeTrack].order)+"/"+(songAmount-1);
  //格式化后的歌曲长度
  dur = FormatTime(s.duration/1000);
  //每次显示的歌曲长度要比真实长度多1秒,否则歌曲播放完了还没有立刻载入下一首歌,
  //此时显示的数字不正常,是0:0.1
  addtime = (s.duration+1000)/1000;
  //第一首歌曲播放结束后继续播放或者播放其他歌曲
  s.onSoundComplete = function() {
    time += 1;
//判断歌曲播放次数是否大于设置的播放次数,如果大与设置数则播放下一首,否则继续播放这首歌
    if (time<=num) {
      s.loadSound(mySongs[activeTrack].file, true);
      addtime += (s.duration+1000)/1000;
      curentsong = (mySongs[activeTrack].order)+"/"+(songAmount-1);
      // first song loops
    } else {
   //还原播放次数值
     time = 1;
     //继续判断是随机播放还是顺序播放
     if (_root.pref.rplay.getState()) {
       activeTrack = random(songAmount-1);
     } else {
//如果当前曲目编号小于总曲目数则播放下一首歌,否则返回播放第一首歌
      if (activeTrack<(mySongs.length-1)) {
          activeTrack += 1;
          // next song
      } else {
          activeTrack = 0;
      }
    }
    //播放下一首歌
    s.loadSound(mySongs[activeTrack].file, true);
    curentsong = (mySongs[activeTrack].order)+"/"+(songAmount-1);
    addtime += (s.duration+1000)/1000;
    _root.enDisplay.t1.show1.text = mySongs[activeTrack].english;
    _root.chDisplay.t2.show2.text = mySongs[activeTrack].chinese;
   }
  };
  // all sounds loop completed!
  delete p;
};
//定义点击下一首歌曲按钮时激发的函数
nextTrack = function () {
  if (activeTrack<(mySongs.length-1)) {
    activeTrack++;
  } else {
    activeTrack = 0;
 }
 num = _root.pref.playtime.getValue();
 playTrack(num);
};
//定义点击上一首歌曲按钮时激发的函数
prevTrack = function () {
  if (activeTrack>0) {
   activeTrack--;
  } else {
   activeTrack = (mySongs.length-1);
  }
  num = _root.pref.playtime.getValue();
  playTrack(num);
};
3.2 界面上几个按钮上的Actions:
播放按钮(图5)上的Action:
on (release) {
   //先停止播放正在播放的歌曲,防止重叠
   s.stop();
   //让音频动态效果打开
   rhythm.b1.play();
   rhythm.b2.play();
   rhythm.b3.play();
   rhythm.b4.play();
   rhythm.b5.play();
   rhythm.b6.play();
   rhythm.b7.play();
   //定义歌曲播放次数变量,其值从设置面板的下拉列表中得到
   num = _root.pref.playtime.getValue();
   //播放状态为开始
   _root.begin = true;
   //调用播放程序
   playTrack(num);
   //播放按钮变为不可见,露出停止按钮
   btnplay._visible = false;
}
下一首歌按钮(图6)和前一首歌按钮(图7)上的Action和播放按钮上的相似,只是调用的程序不同而已。
再来看设置按钮(图8)上的Action:
on(release){
  //还是要停止歌曲,否则设置不起作用
  s.stop();
  //歌曲停放
  _root.begin = false;
  //让播放按钮露出来
  btnplay._visible = true;
  //音频效果停止
  rhythm.b1.stop();
  rhythm.b2.stop();
  rhythm.b3.stop();
  rhythm.b4.stop();
  rhythm.b5.stop();
  rhythm.b6.stop();
  rhythm.b7.stop();
  //设置面板变为可见
  pref._visible = true;
  //设置界面上的按钮在此时不可用,否则在设置面板中移动鼠标会有小手出现
  btnset.enabled = false;
  btnplay.enabled = false;
  btnstop.enabled = false;
  btnstop.enabled = false;
  btnstop.enabled = false;
}
3.3 再看界面上的其他符号的Action
时间显示符号上的Action:
//影片每播放一帧时都要执行判断
onClipEvent (enterFrame) {
  //如果开始播放则记时器开始工作
  if (_root.begin) {
    time = (getTimer()-b)/1000;
    //时间显示结果为歌曲长度减去按钮按下后持续的时间,所以是倒记时显示
    cd.text = _root.FormatTime(_root.addtime-time);
   } else {
     //如果歌曲没有播放记时显示为00:00
     cd.text = _root.FormatTime(0);
     b = getTimer();
   }
}
音频效果MC的Action:
//歌曲没有播放时,柱形不变化
onClipEvent(load){
  b1.stop();
  b2.stop();
  b3.stop();
  b4.stop();
  b5.stop();
  b6.stop();
  b7.stop();
}
两个矩形的文字遮盖影片片段,其Action为:
onClipEvent(load){
  //影片片段被载入时被设置为不可见,即不遮挡文字
  _visible = false;
}
3.4 矩形框中滚动文字的Actions
英文文字的Action:
onClipEvent (enterFrame) {
//如果文字MC的左端向左跑到了等于自己长度的位置上,
//则退回到矩形方框的右端,矩形方框的长度为100pixels
  if (t1._x<-t1._width) {
    t1._x = 100;
  } else {
  //否则文字MC以滑杆设定的速度向左运动
    t1._x -= _root.speedCon.speed;
  }
}

中文文字的Action:
onClipEvent (enterFrame) {
 if (t2._x<-t2._width) {
   t2._x = 100;
  } else {
   t2._x -= _root.speedCon.speed;
  }
}
3.5两个滑杆按钮的修改与使用
3.5.1 控制文字滚动滑杆
为了让大滑杆按钮能控制文字滚动速度,需要做一些修改和添加。先看舞台上滑杆的Action:
//这几行代码是原来没有的
onClipEvent(load){
  //初始速度是3pixels/帧
  speed = 3;
}
onClipEvent(enterFrame){
  //调节滑杆后的速度为3~103,在影片片段fader-mixer中,实例vertFader的_y值
  //会在拖动过程中由top到bottom变化,变化幅度为200pixels,所以要除以2使变化速度不太明显
  speed = 3+(vertFader._y-vertFader.top)/2;
}
再打开影片片段fader-mixer中滑块上的Actions:
onClipEvent (load) {
 inity = _y;
 left = _x;
 right = _x;
 //原来是top = _y-50,这是将滑块放到中间
 top = _y;
 //原来是bottom = _y+50;
 bottom = _y+200;
}
为了能产生播放器中的滑杆效果还需在外观上修改,首先在fader-mixer符号中将滑杆轨道变为2倍原长并然后将滑块变小使之匹配,然后在舞台上逆时针旋转90度,这样就可以了。
3.5.2 控制音量滑杆
使用小滑块控制音量的大小,为了能让它的个头变小,除了用变形工具缩小一半并旋转,还需要在Action上动番手脚,在库中打开滑块fader-gain,看到其最上层被修改过的Actions:
top = vol._y;
left = vol._x;
right = vol._x;
//原来是bottom = vol._y+100
bottom = vol._y+50;
level = 100;

vol.onPress = function() {
  startDrag("vol", false, left, top, right, bottom);
  dragging = true;
  };
  vol.onRelease = function() {
  stopDrag();
  dragging = false;
};
vol.onReleaseOutside = function() {
   dragging = false;
};
this.onEnterFrame = function() {
  if (dragging) {
    //原来是level = 100-(vol._y-top)
    level = 100-(vol._y-top)*2;
  } else {
   if (level>100) {
     level = 100;
   } else if (level<0) {
     level = 0;
   } else {
     //原来是vol._y = -level+100+top;
     vol._y = (-level+100)/2+top;
   }
 }
 //原来是sound.setVolume(level);
 _root.s.setVolume(level);
};
3.6 设置面板中按钮的Action以及组件的状态
打开符号库中的preference文件夹,双击Preference影片片段进入其编辑环境
查看OK按钮上的Action:
on (release) {
 //设置好后,点击OK,设置面板隐藏起来
 this._visible = false;
 //让主界面上的按钮恢复使用
 _root.btnset.enabled = true;
 _root.btnplay.enabled = true;
 _root.btnstop.enabled = true;
 _root.btnnext.enabled = true;
 _root.btnpre.enabled = true;
 //让主界面中矩形文字遮盖区的可见状态从复选框组件中得到布尔值
 _root.tm1._visible = _root.pref.ehide.getValue();
 _root.tm2._visible = _root.pref.chide.getValue();
 //将下拉列表组件中的指示值赋给播放次数变量
 num = _root.pref.playtime.getValue();
}
此外,单选框中设定的布尔值会由playtrack函数在每次执行时读取
 
4.歌曲列表问题
歌曲列表文件的格式为:
<?xml version="1.0" ?>
<!--根节点为songs-->
<songs>
<!--根节点下有歌曲节点Entry,有几个Entry就有几首歌-->
<Entry>
<!--歌曲节点下又有三个歌曲信息节点-->
<Source>MP3文件的相对路径</Source>
<Name>英文歌名</Name>
<Cn>中文歌名</Cn>
</Entry>
<Entry>
......
</Entry>
......
......
</songs>
歌曲列表文件要想被Flash正确的读取,必须保存成unicode编码格式,或UTF-8编码格式。用WinXP中的记事本即可编辑此歌曲列表文件,保存时需要选择保存类型为“所有文件”,编码选择“UTF-8”这样生成的XML文件比用unicode编码小一些,注意文件名的结尾必须输入.xml,这样才能生成XML文件。
再看Flash是如何解析XML文件的:
//定义解析XML文件所用的函数
function musicParser(branch) {
 //定义一个数组用于存放播放歌曲时用到的信息,比如路径、文字。
mySongs = new Array();
//现在指向了根节点的第一个子节点,即第一个&lt;Entry>.....&lt;/Entry>
songList = branch.firstChild;
//采用循环操作依次读取列表中的信息并放入mySongs数组中,
//branch.childNodes.length是&lt;Entry>.....&lt;/Entry>的数目
for (i=0; i&lt;branch.childNodes.length; i++) {
//songList.firstChild指向歌曲文件路径节点&lt;Source>MP3文件的相对路径&lt;/Source>,
//songList.firstChild.firstChild指向“MP3文件的相对路径”节点,然后它的nodeValue才是可用的路径信息。
source = songList.firstChild.firstChild.nodeValue;
series = i+1;
//nextSibling用于将指针从路径节点移到名字节点
eshow = songList.firstChild.nextSibling.firstChild.nodeValue;
//lastChild用于将指针移到当前歌曲下的最后一个节点,即中文名字节点
cshow = songList.lastChild.firstChild.nodeValue;
mySongs[i] = {file:source, order:series, english:eshow, chinese:cshow};
//将指针从当前歌曲节点移到下一个歌曲节点&lt;Entry>.....&lt;/Entry>
songList = songList.nextSibling;
}
}
//定义用于容纳歌曲列表文件music.xml中歌曲信息的XML对象
xmlObject = new XML();
//忽略空白节点内容
xmlObject.ignoreWhite = true;
//如果xmlObject对象加载music.xml文件成功,返回给myOnload函数"true",否则返回"false"
xmlObject.onLoad = myOnLoad;
//载入歌曲列表文件
xmlObject.load("music.xml");
 
5.致谢
到这里,播放器中涉及到的所有难点要点就解释完了,此时再动手做就很简单了。
本播放器之所以能做出来要感谢两个人,一个是作品所涉及的关键素材,播放函数playTrack及XML文件解析程序是来自 瑞士人Roland Schaer 的一个组件:FSound V1.0.0 中的部分代码。他的 E-Mail: roele33@hotmail.com ,在 Internet: www.rolandschaer.ch 上有他的其他信息,可惜我一直没有时间去看看。
还要感谢我的女友,在我2003年底创作这个小程序时对我不断的鼓励。一个多月的啃代码,让我从不懂ActionScript到熟练使用,并喜欢上了AS编程。没有这两个人的贡献就不会有现在这个作品呈献给大家。

视频教程列表
文章教程搜索
 
Flash AS推荐教程
Flash AS热门教程
看全部视频教程
购买方式/价格
购买视频教程: 咨询客服
tel:15972130058