指令与服务

因为音乐播放器需要操纵DOM元素,所以不应该在控制器中直接操控,于是可以利用服务和指令来实现.这里利用指令来实现

模板样式

<div class="music-play"> 
   <p>{{now_music.name}}</p> 
   <audio class="music_audio" ng-src="{{now_music.src}}"></audio> 
   <div class="bar " style="width: 100%"> 
       <span class="bar-bg"> 
           <span class="bar-on music-play-progress"></span> 
       </span> 
   </div> 
   <div class="music-play-ctrl"> 
       <div class="btn-group"> 
           <button class="btn btn-default" ng-click="musicPref()"> 
               <span class="glyphicon glyphicon glyphicon-step-backward"></span> 
           </button> 
           <button class="btn btn-default" ng-click="musicPlay()"> 
               <span class="glyphicon " ng-class="playClass?'glyphicon-pause':'glyphicon-play'"></span> 
           </button> 
           <button class="btn btn-default" ng-click="musicNext()"> 
               <span class="glyphicon glyphicon glyphicon-step-forward"></span> 
           </button> 
       </div> 
   </div> 
</div> 

<ul class="list-group roomNumber"> 
   <li class="list-group-item " ng-repeat="music in music_list "> 
       <div class="row"> 
           <div class="col-sm-7" ng-click="play(music)" ng-class="now_music==music?'now_play':'ttt' ">{{music.name}} 
           </div> 
       </div> 
   </li> 
</ul> 

其中now_music为当前歌曲 为一个object;music_list为歌曲列表

写个指令

app.directive("musicPlayer", function ($timeout) { 
   return { 
       restrict: 'EA', 
       templateUrl: '/musicPlay.html', 
       controller: function ($scope) { 
         //数据交换操纵 
       }, 
       link: function (scope, element, attr) { 
         //DOM操纵 
       } 

指令这块写的不是太好 因为就算是DOM操纵也涉及到了一部分的数据交换,仅仅是把涉及到DOM操纵的数据交换放link里,毕竟link一但编译后就不变动了比如你在link里写个代码

$("p").html($scope.name) 

然后在controller里写个定时器 每秒加1

$scope.name=0 
$timeout(function(){ 
   $scope.name++ 
 },1000) 

然后刷新下页面后,发现在p标签中有个0,但是一秒后这个0并不会变

controller数据交换

添加歌曲列表

$scope.$on("musicList", function (event,data) { 
   $scope.music_list = data 
} 

我采用的是广播的形式来获取歌曲列表 实际上也可以通过ajax获取后赋值,因为要拿到值后才能做剩下的动作 所以controller层的所有代码都在 $scope.$on里

设置当前歌曲时间信息

$scope.pointer = 0 //歌曲指针 
$scope.now_music = $scope.music_list[$scope.pointer] //当前歌曲 

//歌曲当前时间 
$scope.progress_time = { 
   now: 0, 
   true_time: 0, 
   end: 0, 
} 

true_time用来判断歌曲剩下的时间

播放与暂停

//播放与暂停 
$scope.musicPlay = function () { 
   //只有当歌曲触发了"canplay"事件才能点击 
   if ($scope.progress_time.end == 0) { 
       console.log("未准备") 
       return false 
   } else if ($scope.playClass) { 
       $scope.audio.pause(); 
       $scope.playClass = false 
       $scope.clearAudioProgress(); 
       console.log("暂停") 
   } else if ($scope.playClass == false) { 
       //获取歌曲当前时间 
       $scope.getNowTime() 
       $scope.progress_time.true_time = ($scope.progress_time.end - $scope.progress_time.now) * 1000 
       $scope.setAudioProgress($scope.progress_time.true_time) 
       $scope.audio.play(); 
       $scope.playClass = true; 
       console.log("开始") 
   } 
} 

因为播放涉及到一个audio标签的事件也就是”canplay”,所以这里的播放暂停是针对 触发了canplay后的暂停与播放 什么意思呢?比如歌曲没触发canplay事件之前 暂停与播放都是返回false至于$scope.clearAudioProgress()和setAudioProgress()看下面代码当歌曲触发了canplay事件后会设置$scope.progress_time.endplayClass为播放按钮样式,

下一曲

//下一曲 
$scope.musicNext = function () { 

   if ($scope.pointer >= $scope.music_list.length - 1) { 
       return false 
   } 

   //设置歌曲与进度条为初始状态 
   $scope.clearAudioProgress(true) 
   $scope.setNowTime(0) 
   $scope.pointer++ 
   $scope.now_music = $scope.music_list[$scope.pointer] 
} 

先判断歌曲是否为列表最后一个歌曲了,如果不是那就设置一下进度条更新下now_music

上一曲

    //上一曲 
   $scope.musicPref = function () { 
       if ($scope.pointer <= 0) { 
           return false 
       } 
       //设置歌曲与进度条为初始状态 
       $scope.clearAudioProgress(true) 
       $scope.setNowTime(0) 
       $scope.pointer-- 
       $scope.now_music = $scope.music_list[$scope.pointer] 
   } 
}) 

同 下一曲

link DOM操纵区

获取audio标签和进度条标签

//关于DOM操作都在这块 
scope.audio = $(".music_audio")[0] 
scope.audio_progress = $(".music-play-progress")[0]; 

触发了canplay事件

//当歌曲触发了canplay事件才能播放 
$(scope.audio).on("canplay", function () { 
   //重置歌曲信息 
   scope.progress_time.now = scope.audio.currentTime = 0 
   scope.progress_time.true_time = 0 
   scope.progress_time.end = scope.audio.duration 
   //清除进度条 
   scope.clearAudioProgress() 
   scope.audio.play() 
   scope.playClass = true; 
   //设置进度条 
   scope.progress_time.true_time = (scope.progress_time.end - scope.progress_time.now) * 1000 
   scope.setAudioProgress(scope.progress_time.true_time) 
   scope.$apply() 
}) 

清除进度条当前状态

//清除进度条当前状态 
scope.clearAudioProgress = function (val) { 
    $(scope.audio_progress).stop(true) 
    if (val) { 
        $(scope.audio_progress).width(0) 
    } 
} 

传递了不为false的val会清除进度条 否则仅仅是停止进度条动画

进度条动画

//设置进度条 
scope.setAudioProgress = function (time, cb) { 
   $(scope.audio_progress).stop(true) 
   $(scope.audio_progress).animate({ 
       width: "100%" 
   }, time, function () { 
       scope.musicNext() 
   }) 
} 

获取与设置歌曲当前时间

//获取歌曲当前时间 
scope.getNowTime = function () { 
   return scope.progress_time.now = scope.audio.currentTime 
} 

//设置歌曲当前时间 
scope.setNowTime = function (val) { 
   scope.progress_time.now = scope.audio.currentTime = val 
} 

后记

其实这个例子待改进的地方很多 在link区可以var一个object然后$scope.musicServer=object 返回一个object 这样就不会暴露很多的函数.