      1 /**
      2  * Hilo
      3  * Copyright 2015 alibaba.com
      4  * Licensed under the MIT License
      5  */
      6
      7 /**
      8  * @class HTMLAudio声音播放模块。此模块使用HTMLAudioElement播放音频。
      9  * 使用限制：iOS平台需用户事件触发才能播放，很多Android浏览器仅能同时播放一个音频。
     10  * @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。
     11  * @module hilo/media/HTMLAudio
     12  * @requires hilo/core/Class
     13  * @requires hilo/util/util
     14  * @requires hilo/event/EventMixin
     15  * @property {String} src 播放的音频的资源地址。
     16  * @property {Boolean} loop 是否循环播放。默认为false。
     17  * @property {Boolean} autoPlay 是否自动播放。默认为false。
     18  * @property {Boolean} loaded 音频资源是否已加载完成。只读属性。
     19  * @property {Boolean} playing 是否正在播放音频。只读属性。
     20  * @property {Number} duration 音频的时长。只读属性。
     21  * @property {Number} volume 音量的大小。取值范围：0-1。
     22  * @property {Boolean} muted 是否静音。默认为false。
     23  */
     24 var HTMLAudio = Class.create(/** @lends HTMLAudio.prototype */{
     25     Mixes: EventMixin,
     26     constructor: function(properties){
     27         util.copy(this, properties, true);
     28
     29         this._onAudioEvent = this._onAudioEvent.bind(this);
     30     },
     31
     32     src: null,
     33     loop: false,
     34     autoPlay: false,
     35     loaded: false,
     36     playing: false,
     37     duration: 0,
     38     volume: 1,
     39     muted: false,
     40
     41     _element: null, //HTMLAudioElement对象
     42
     43     /**
     44      * 加载音频文件。
     45      */
     46     load: function(){
     47         if(!this._element){
     48             var elem;
     49             try{
     50                 elem = this._element = new Audio();
     51                 elem.addEventListener('canplaythrough', this._onAudioEvent, false);
     52                 elem.addEventListener('ended', this._onAudioEvent, false);
     53                 elem.addEventListener('error', this._onAudioEvent, false);
     54                 elem.src = this.src;
     55                 elem.volume = this.volume;
     56                 elem.load();
     57             }
     58             catch(err){
     59                 //ie9 某些版本有Audio对象，但是执行play,pause会报错！
     60                 elem = this._element = {};
     61                 elem.play = elem.pause = function(){
     62
     63                 };
     64             }
     65         }
     66         return this;
     67     },
     68
     69     /**
     70      * @private
     71      */
     72     _onAudioEvent: function(e){
     73         // console.log('onAudioEvent:', e.type);
     74         var type = e.type;
     75
     76         switch(type){
     77             case 'canplaythrough':
     78                 e.target.removeEventListener(type, this._onAudioEvent);
     79                 this.loaded = true;
     80                 this.duration = this._element.duration;
     81                 this.fire('load');
     82                 if(this.autoPlay) this._doPlay();
     83                 break;
     84             case 'ended':
     85                 this.playing = false;
     86                 this.fire('end');
     87                 if(this.loop) this._doPlay();
     88                 break;
     89             case 'error':
     90                 this.fire('error');
     91                 break;
     92         }
     93     },
     94
     95     /**
     96      * @private
     97      */
     98     _doPlay: function(){
     99         if(!this.playing){
    100             this._element.volume = this.muted ? 0 : this.volume;
    101             this._element.play();
    102             this.playing = true;
    103         }
    104     },
    105
    106     /**
    107      * 播放音频。如果正在播放，则会重新开始。
    108      * 注意：为了避免第一次播放不成功，建议在load音频后再播放。
    109      */
    110     play: function(){
    111         if(this.playing) this.stop();
    112
    113         if(!this._element){
    114             this.autoPlay = true;
    115             this.load();
    116         }else if(this.loaded){
    117             this._doPlay();
    118         }
    119
    120         return this;
    121     },
    122
    123     /**
    124      * 暂停音频。
    125      */
    126     pause: function(){
    127         if(this.playing){
    128             this._element.pause();
    129             this.playing = false;
    130         }
    131         return this;
    132     },
    133
    134     /**
    135      * 恢复音频播放。
    136      */
    137     resume: function(){
    138         if(!this.playing){
    139             this._doPlay();
    140         }
    141         return this;
    142     },
    143
    144     /**
    145      * 停止音频播放。
    146      */
    147     stop: function(){
    148         if(this.playing){
    149             this._element.pause();
    150             this._element.currentTime = 0;
    151             this.playing = false;
    152         }
    153         return this;
    154     },
    155
    156     /**
    157      * 设置音量。注意: iOS设备无法设置音量。
    158      */
    159     setVolume: function(volume){
    160         if(this.volume != volume){
    161             this.volume = volume;
    162             this._element.volume = volume;
    163         }
    164         return this;
    165     },
    166
    167     /**
    168      * 设置静音模式。注意: iOS设备无法设置静音模式。
    169      */
    170     setMute: function(muted){
    171         if(this.muted != muted){
    172             this.muted = muted;
    173             this._element.volume = muted ? 0 : this.volume;
    174         }
    175         return this;
    176     },
    177
    178     Statics: /** @lends HTMLAudio */ {
    179         /**
    180          * 浏览器是否支持HTMLAudio。
    181          */
    182         isSupported: window.Audio !== null
    183     }
    184
    185 });