让我们来学会一些判断问题的方法和培养一些直觉。
首先,学会观察你的问题出现的情况:只是在重复(或者说再次)点击播放按钮时才会出现问题。而无论试多少遍,在第一次点击播放按钮的时候不会有任何问题。而且它们的时间显示一直很正常。而即使你在第二次点击播放按钮后,它们的时间间隔跳动也很规律。(虽然显示的时间不对)
“在第一次点击播放按钮的时候不会有任何问题。”和“而即使你在第二次点击播放按钮后,它们的时间间隔跳动也很规律。”这是两条很重要的信息。事实上它隐藏了你的上面的关于时间换算的代码是正确的。确实,无论你无何修改那段代码,这个问题也依然存在。(但这里你不要学会一个误区,认为你改进代码后你的问题依然存在就认为这里就没有问题。有时可能你改过后的代码仍然是错误的,这时你所要做的是:再次认真的审视问题出现的情况,然后冷静的思考。重新判断问题所在)
你可能已注意到了下面的信息:
只是在重复(或者说再次)点击播放按钮时
在第二次点击播放按钮
注意一些关于Sound.position的信息
如果你猜测这是问题的关键所在,那么,你应该试试。
从这里判断这应该是一些初始化的问题。(至少在中间过程中不会这样)。那么考虑在代码中加上一些初始化的代码。考虑在上面的代码中加入,但因为它是处于onEnterFrame事件中,似乎有点不太可能。此时你可能会去查找一些关于Sound对象的帮助。
如果你注意了这里面的信息:
Sound.position 属性(只读);返回声音已播放的毫秒数。如果声音是循环的,则在每次循环开始时,位置将被重置为 0。
“重置为0“是个很重要的信息。这也意味着你的Sound在第二次加载时可能没有被重置。尝试一些重置的方法:
delete mySound;
mySound = new Sound();
它不会对你现有的东西有什么影响,它的含义是,先将原来的mySound对象(你的MP3)从内存中删除,然后重新建立一个。
既然它不会对你现有的东西有什么影响,为什么不试试呢?
这是改写后的播放按钮的完整代码:
on (release) {
delete mySound;
mySound = new Sound();
mySound.loadSound("UploadFile/mp3player/musics/1.mp3", true);
}
事实最终证明你的判断和修改是正确的:它不会再出现这种情况。
好了,从这里你也应该注意到了一些:内存中缓存对你的影响。
好了,开始我们的进度条部份:
关于如何制作一个类似的按钮你可以参考第一章的扩展部分: flash基本软件操作和界面设计技巧(应该让你的按钮更易于使用)
我将这个可以拖动的按钮做成一个电影剪辑(因为这样意味着可以被瞄准和控制坐标),并命名为bar,然后再将它转换成一个电影剪辑,并命名为playBar。这样一来,bar的默认X初始位置为0,这会方便以后的计算。
这是在按钮上的ActionScript:
on (press) {
startDrag(this, false, 0, 0, 170, 0);
}
on (release, releaseOutside) {
stopDrag();
}
这些ActionScript使你可以在按钮被按下(press)时可以被在X=0-170这个范围内拖动。this指代按钮所在TimeLine的影片剪辑bar,拖动它,也就相当于拖动了这个按钮。
on (release, releaseOutside) {
stopDrag();
}
使你在放开时停止这种拖动。
我们来看更新进度条位置的ActionScript:
playBar.bar._x = (mySound.position/mySOund.duration)*170;
这里,我们获得当前所在时间和总时间的比例值,用它乘以一个在最终位置时的值(在这里是X=170)。因为我们刚才将bar做成了一个影片剪辑,所以这里就可以不必再进行额外的差值运算。
因为我们应该每时每刻(在Flash中是进入每帧时)都更新它的位置,所以我将它放在更新时间显示的onEnterFrame事件中(也就是函数updateSoundTime)
这里会出现一个同步错误,如果你测试过你的mp3播放器,你会发现它当开始播放mp3后,你的进度条。不能被拖动,因为你的_root上每帧都会更新进度条的值,所以“即使你拖动了它,它也马上会被刷新而变成playBar.bar._x = (mySound.position/mySOund.duration)*170;这里的值。
要解决这个问题,你可以使用这种方法:用一个变量来监控它的状态。
具体的做法是:
在进度条被按下时将变量_root.dragBar设为1;然后在放开后将它设为0;
这是改进后在按钮上的代码:
on (press) {
_root.dragBar = 1;
startDrag(this, false, 0, 0, 170, 0);
}
on (release, releaseOutside) {
_root.dragBar = 0;
stopDrag();
}
然后改进你的进入每帧时更新进度条的代码,使它在_root.dragBar不等于1的时候才更新进度条的位置。
这是改进后的代码:
if (_root.dragBar != 1) {
playBar.bar._x = (mySound.position/mySOund.duration)*170;
}
现在它执行得很好。
到此为止,你只剩下最后一组任务了,现在应该解决在拖动这个滑块(刚才建立的那个按钮)后,让它到指定的位置播放。和你的暂停按钮。
事实上,你可以将这部份的代码写在按钮中,也可以将它写在_root中,封装在一个函数playSoundAt()中。然后由按钮调用。这里我们选择_root。
这是函数playSoundAt()的代码(它其实就是更新进度条位置的反运算,再加上flash中一个内建的Sound.start()方法,:)flash中内建的方法更像是一些驱动和接口)
function playSoundAt() {
var tmpA = Math.floor(playBar.bar._x/170*mySound.duration/1000);
mySound.stop();
mySound.start(tmpA);
_root.dragBar = 0;
}
注意Sound.start的单位是秒,而不是毫秒,所以你要将它除以1000以转换,用Math.floor()是为了避免你最后在Sound.start中使用的值会出现小数。
Wo...我需要告诉你一些关于Flash Player6和Flash Player7在处理MP3上的差异。在Flash Player6中,一个声音只有在加载时设为“事件声音”才能使用Sound.start拖动定位。而流式声音则不行。 这实在是个很残酷的功能。
这也意味着在Flash Player6中你只有把你的“播放”按钮改成这样才能使你的进度条在拖动时起作用,否则声音会“意外”地停止。
on (release) {
delete mySound;
mySound = new Sound();
mySound.loadSound("UploadFile/mp3player/musics/1.mp3", false);
}
事实上,用这种方法加载的声音(事件声音)需要在声音下载完才能播放。而且它它不会在声音下载完自动播放(Sound.onLoad事件也无法完成这个功能)。你需要用手动的方法来播放它。当然,代替手动的方法你可以建立一个每帧检测事件(Sound.getBytesLoaded 和 Sound.getBytesTotal ),如果下载完了,则开始播放它。
如果你加载的MP3在硬盘中,那么你可以简化这个MP3 Loading检测。因为在你硬盘中的MP3的加载几乎不需要延迟,所以你可以直接将代码写成这样:
on (release) {
delete mySound;
mySound = new Sound();
mySound.loadSound("http://5istudio.flashk.com/FK/UploadFile/mp3player/musics/1.mp3"'' target="_blank" >http://5istudio.flashk.com/FK/UploadFile/mp3player/musics/1.mp3";;, true);
mySound.start();
}
而在Flash Player7 中无论你使用事件声音还是流式声音你都可以使用Sound.start来定位。当然,它带来的好处相当明显(特别是在低带宽的环境中):可以边下载边播放,而你你可以拖动播放进度条。而在Flash Player6中,你只能忍痛在其中选择一种,而将要抛弃另外的那个功能。
当然,如果要在边下载边播放的环境中使播放更加顺畅,可以使用_soundbuftime属性,它指示了声音流缓冲的秒数。默认值为 5 秒。 你可以将它设为更高的值(_soundbuftime=10),以使它在更低的带宽中可以流畅的播放。至于在何种带宽环境下应该使用何种值我不能给你一个明确的答案,最好的方法就是你自已去试验它。为了更智能的使用这个属性以使不同的使用者都流畅的播放,你可以用一个方法来检测用户的带宽(在你加载MP3的时候就可以使用Sound.getBytesLoaded 和getTimer来检测用户的带宽,当然,如果你的服务器太慢则可能是你的服务器的带宽,记住水桶效应,但不管怎么说,这得到的都是当前状态下的传输速率),然后动态的设置_soundbuftime的值。
我推荐你使用Flash Player7来使用这个播放器,尽管它在Flash Player6中绝大部分也能工作。(很抱歉,我没有专门为Flash Player6考虑,因为它会增加教程的难度)
好了,回来。现在如果测试你的播放器会出现一些问题:当你拖动放开你的进度条时,它会在放开的时候有时会有些抖动。
为了节约些许的时间,我直接告诉你造成问题的原因:你无法确保你的更新进度条的代码在同一帧中比定位MP3时间的代码先执行还是后执行。
if (_root.dragBar != 1) {
playBar.bar._x = (mySound.position/mySound.duration)*170;
}
对比:
var tmpA = Math.floor(playBar.bar._x/170*mySound.duration/1000);
mySound.stop();
mySound.start(tmpA);
_root.dragBar = 0;
仅管你已经在定位MP3时间的代码中将_root.dragBar = 0在定位完后才释放更新进度条代码的执行权。但并不意味着执行mySound.start(tmpA);不需要一些时间。(事实上,这有时不尽相同,你可以从你的抖动的不定期看出来)
解决的办法我使用了setInterval()动作。有关setInterval()动作的详细说明请查找教程关于这章的扩展 setInterval:超越帧速率的限制。
这里使用的其实是一个setInterval()的特殊变种:将它变成延迟执行的方法。
这是最终的包括playSoundAt()的代码:
function playSoundAt() {
var tmpA = Math.floor(playBar.bar._x/170*mySound.duration/1000);
mySound.stop();
mySound.start(tmpA);
startLockUpdate();
}
function startLockUpdate() {
setInterval_me = setInterval(unLockLater, 500, true);
}
function unLockLater(n) {
_root.dragBar = 0;
if (n) {
clearInterval(setInterval_me);
}
}
我让_root.dragBar 在经过500毫秒(0.5秒)后才释放更新进度条代码的执行权。这时Flash已经运行了20帧。所以它不会再出现冲突问题。而你的用户对这0.5秒几乎不会有什么特别的感觉。
当然,这个使用setInterval()实现的功能你也可以通过一个MC来实现它。但setInterval()带来的好处是你可以很容易的更改延迟的时间和代码(因为它们都在一起,使用MC,你不得不再进入到MC中编辑)。
完成暂停按钮。
on (release) {
mySound.stop();
hasPause = 1;
_root.dragBar = 1;
}
它很简单。mySound.stop()将声音停止。
_root.dragBar = 1然后锁定更新进度条代码的执行权。
并记录一个暂停的状态hasPause = 1。
虽然暂停按钮很简单,但却会引起一些较大的代码变化。现在,你的播放按钮同时要控制两个按钮--暂停和停止。并跟据暂停和停止各自在按下后再按下播放时实现不同的功能:
当暂停按下时只是继续播放MP3;
当停止按下时重新载入MP3,并将mySound初始化。
这是暂停按钮给播放按钮带来的变化(注意else if,它使你在按下暂停后再按下停止不会产生冲突):
on (release) {
if (hasStop == 1) {
delete mySound;
mySound = new Sound();
mySound.loadSound("http://5istudio.flashk.com/FK/UploadFile/mp3player/musics/1.mp3"'' target="_blank" >http://5istudio.flashk.com/FK/UploadFile/mp3player/musics/1.mp3";;, true);
_root.dragBar = 0;
hasStop = 0;
} else if (hasPause == 1) {
playSoundAt();
_root.dragBar = 0;
hasPause = 0;
}
}
这是暂停按钮给停止按钮带来的变化:
on (release) {
mySound.stop();
playBar.bar._x = 0;
_root.dragBar = 1;
hasStop = 1;
}
关于初始化场景:
Stage.scaleMode = "noScale";
Stage.showMenu = 0;
Stage.scaleMode = "noScale";将场景的缩放设为100%模式;
Stage.showMenu = 0;将隐藏Flash的右键菜单中的控制项(你以后可以不必在DreamWeaver里设置),并将Flash独立播放器中的菜单隐藏。
这个V2.0版的播放器中可能引入了太多的概念以致你无法一下子全吸收它。这里是一些简要内容:
界面设计师和程序设计师和分工和合作
设计规划你的程序和项目的重要性
变通和多种方法
学会判断某些错误的关键所在
使用功能模块和状态控制
修改一个功能可能对其它部份造成的影响
[1] [2]
