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