create-inner-audio-context.uvue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. <template>
  2. <!-- #ifdef APP -->
  3. <scroll-view style="flex: 1;">
  4. <!-- #endif -->
  5. <view class="uni-padding-wrap">
  6. <page-head title="audio"></page-head>
  7. <view class="uni-common-mt">
  8. <slider ref="slider" :value="position" :min="0" :max="duration" @changing="onchanging"
  9. @change="onchange"></slider>
  10. </view>
  11. <view class="uni-title">
  12. <text class="uni-title-text">属性示例</text>
  13. </View>
  14. <text class="uni-text-box uni-common-mt">当前音频播放位置(保留小数点后 3 位):{{currentTime}} s</text>
  15. <text class="uni-text-box">音频的长度(单位:s):{{duration}} s</text>
  16. <text class="uni-text-box">当前是否停止状态:{{isPaused}}</text>
  17. <text class="uni-text-box">音频缓冲的时间点:{{buffered}} s</text>
  18. <text class="uni-text-box">当前音量:{{volume}}</text>
  19. <button plain :disabled="volume == 1" @click="increaseVolume">增加音量</button>
  20. <button plain :disabled="volume == 0" @click="decreaseVolume">减少音量</button>
  21. <text class="uni-subtitle-text uni-title">开始播放的位置(单位:s)</text>
  22. <input :value="startTime" type="number" placeholder="开始播放的位置(单位:s)" class="uni-input"
  23. @input="startTimeInput"></input>
  24. <boolean-data :defaultValue="false" title="是否自动开始播放" @change="setAutoplay"></boolean-data>
  25. <boolean-data :defaultValue="false" title="是否循环播放" @change="setLoop"></boolean-data>
  26. <text class="uni-subtitle-text uni-title"
  27. style="padding-left: 10px;padding-top: 10px;padding-right: 10px;">播放倍率(Web/HarmonyOS 不支持)</text>
  28. <radio-group class="uni-flex uni-row radio-group" @change="playbackRateChange"
  29. style="flex-wrap: wrap;padding: 10px;">
  30. <radio value="0.5" style="margin-right: 3px">0.5
  31. </radio>
  32. <radio value="0.8" style="margin-right: 3px">0.8</radio>
  33. <radio value="1.0" style="margin-right: 3px" :checked="playbackRateChecked">1.0</radio>
  34. <radio value="1.25" style="margin-right: 3px">1.25</radio>
  35. <radio value="1.5" style="margin-right: 3px">1.5</radio>
  36. <radio value="2.0">2.0</radio>
  37. </radio-group>
  38. <view class="uni-title">
  39. <text class="uni-title-text">方法示例</text>
  40. </View>
  41. <button :disabled="isPlaying" @click="play" class="uni-btn">播放</button>
  42. <button :disabled="!isPlaying" @click="pause" class="uni-btn">暂停</button>
  43. <button :disabled="!isPlaying" @click="stop" class="uni-btn">停止</button>
  44. <button @click="onchangeValue(20)" class="uni-btn">跳转到指定位置20</button>
  45. <button @click="onTimeUpdate" class="uni-btn">onTimeUpdate</button>
  46. <button @click="offTimeUpdate" class="uni-btn">offTimeUpdate</button>
  47. <button @click="onWaiting" class="uni-btn">onWaiting</button>
  48. <button @click="offWaiting" class="uni-btn">offWaiting</button>
  49. <text style="color: red;font-size: 15px;margin-top: 10px;">tip:销毁后请重新进入此界面再播放</text>
  50. <button @click="destory" class="uni-btn">销毁</button>
  51. <view class="uni-title">
  52. <text class="uni-title-text">格式/路径示例</text>
  53. </View>
  54. <navigator url="/pages/API/create-inner-audio-context/inner-audio-format" class="uni-btn">
  55. <button @click="pause">音频格式示例</button>
  56. </navigator>
  57. <navigator url="/pages/API/create-inner-audio-context/inner-audio-path" class="uni-btn">
  58. <button @click="pause">音频路径示例</button>
  59. </navigator>
  60. <navigator url="/pages/API/create-inner-audio-context/inner-audio-mult" class="uni-btn">
  61. <button @click="pause">多音频同时播放</button>
  62. </navigator>
  63. </view>
  64. <!-- #ifdef APP -->
  65. </scroll-view>
  66. <!-- #endif -->
  67. </template>
  68. <script lang="uts">
  69. const audioUrl = 'https://web-ext-storage.dcloud.net.cn/uni-app/ForElise.mp3'
  70. export default {
  71. data() {
  72. return {
  73. title: "innerAudioContext",
  74. currentTime: 0,
  75. duration: 100,
  76. startTime: 0,
  77. buffered: 0,
  78. volume: 0.5,
  79. isCanplay: false,
  80. isPlaying: false,
  81. isPaused: true,
  82. isPlayEnd: false,
  83. _isChanging: false,
  84. _audioContext: null as InnerAudioContext | null,
  85. // 自动化测试
  86. onSeekingTest: false,
  87. onSeekedTest: false,
  88. onWaitingTest: false,
  89. playbackRateChecked: true,
  90. onTimeUpdateCb: (res : any) => { },
  91. onWaitingCb: (res : any) => { }
  92. }
  93. },
  94. computed: {
  95. position() {
  96. return this.isPlayEnd ? 0 : this.currentTime;
  97. },
  98. },
  99. onReady() {
  100. this._audioContext = uni.createInnerAudioContext();
  101. this._audioContext!.src = audioUrl;
  102. this.volume = this._audioContext!.volume;
  103. this.onCanplay()
  104. this._audioContext!.onPlay(() => {
  105. this.isPaused = false;
  106. this.isPlaying = true;
  107. console.log('开始播放', this.isPaused);
  108. });
  109. this.onTimeUpdateCb = (res : any) => {
  110. if (this._isChanging) { return; }
  111. this.currentTime = this._audioContext!.currentTime;
  112. this.buffered = this._audioContext!.buffered;
  113. console.log('onTimeUpdateCb', this.currentTime)
  114. // #ifdef MP
  115. // 微信小程序安卓端过早的时机获取的buffered、duration为0,改为在此处获取
  116. if(this._audioContext!.duration === 0) {
  117. this.buffered = this._audioContext!.buffered;
  118. this.duration = this._audioContext!.duration
  119. }
  120. // #endif
  121. if (this.currentTime > this.buffered) {
  122. console.log('缓冲不足');
  123. }
  124. };
  125. this.onWaitingCb = (res : any) => {
  126. console.log('音频加载中事件');
  127. this.onWaitingTest = true
  128. }
  129. this.onTimeUpdate()
  130. // this.onWaiting()
  131. this.onError()
  132. this.onEnded()
  133. },
  134. onUnload() {
  135. if (this._audioContext != null) {
  136. if (this.isPlaying) {
  137. this.stop();
  138. }
  139. this._audioContext!.destroy()
  140. }
  141. },
  142. methods: {
  143. onCanplay() {
  144. this._audioContext!.onCanplay(() => {
  145. console.log('音频进入可以播放状态事件');
  146. this.isCanplay = true;
  147. // #ifdef MP
  148. // 微信小程序安卓端过早的时机获取的volume为undefine,改为在此处获取
  149. this.volume = this._audioContext!.volume;
  150. // #endif
  151. // 当音频可以播放时,获取缓冲信息
  152. this.buffered = this._audioContext!.buffered;
  153. this.duration = this._audioContext!.duration
  154. });
  155. },
  156. onchanging() {
  157. this._isChanging = true;
  158. },
  159. onchange(e : UniSliderChangeEvent) {
  160. let pos = e.detail.value;
  161. console.log('pos', pos);
  162. this.onSeeking()
  163. this.onSeeked()
  164. this._audioContext!.seek(pos);
  165. this._isChanging = false;
  166. },
  167. onchangeValue(pos : number) {
  168. this.onSeeking()
  169. this.onSeeked()
  170. this._audioContext!.seek(pos);
  171. this._isChanging = false;
  172. },
  173. startTimeInput(e : UniInputEvent) {
  174. let startTimeValue = parseInt(e.detail.value)
  175. this._audioContext!.startTime = startTimeValue;
  176. this.onchangeValue(startTimeValue)
  177. },
  178. setAutoplay() {
  179. this._audioContext!.autoplay = !this._audioContext!.autoplay;
  180. console.log(this._audioContext!.autoplay, 'autoplay');
  181. },
  182. setLoop() {
  183. this._audioContext!.loop = !this._audioContext!.loop;
  184. console.log(this._audioContext!.loop, 'loop');
  185. },
  186. play() {
  187. if (!this.isCanplay) {
  188. uni.showToast({
  189. title: '音频未进入可以播放状态,请稍后再试',
  190. icon: 'error'
  191. });
  192. return;
  193. }
  194. this.isPlaying = true;
  195. this._audioContext!.play();
  196. this.isPlayEnd = false;
  197. if (this._audioContext!.startTime > 0) {
  198. this.onchangeValue(this._audioContext!.startTime)
  199. }
  200. },
  201. onSeeking() {
  202. this._audioContext!.onSeeking(() => {
  203. console.log('音频进行 seek 操作事件');
  204. this.onSeekingTest = true
  205. });
  206. },
  207. onSeeked() {
  208. this._audioContext!.onSeeked(() => {
  209. console.log('音频完成 seek 操作事件');
  210. this.onSeekedTest = true
  211. });
  212. },
  213. onWaiting() {
  214. this._audioContext!.onWaiting(this.onWaitingCb);
  215. },
  216. offWaiting() {
  217. this._audioContext!.offWaiting(this.onWaitingCb);
  218. },
  219. onTimeUpdate() {
  220. this._audioContext!.onTimeUpdate(this.onTimeUpdateCb);
  221. },
  222. offTimeUpdate() {
  223. this._audioContext!.offTimeUpdate(this.onTimeUpdateCb);
  224. },
  225. increaseVolume() {
  226. this.volume = Math.min(this.volume + 0.1, 1);
  227. this.volume = parseFloat(this.volume.toFixed(1));
  228. this._audioContext!.volume = this.volume
  229. console.log('增加音量', this.volume);
  230. },
  231. decreaseVolume() {
  232. this.volume = Math.max(this.volume - 0.1, 0);
  233. this.volume = parseFloat(this.volume.toFixed(1));
  234. console.log('减少音量', this.volume);
  235. this._audioContext!.volume = this.volume
  236. },
  237. onEnded() {
  238. this._audioContext!.onEnded(() => {
  239. console.log('播放结束');
  240. this.currentTime = 0;
  241. this.startTime = 0
  242. this.isPlaying = false;
  243. this.isPaused = true;
  244. this.isPlayEnd = true;
  245. (this.$refs["slider"] as UniSliderElement).value = 0
  246. });
  247. },
  248. onError() {
  249. this._audioContext!.onError((err) => {
  250. console.log('err', err);
  251. this.isPlaying = false;
  252. this.isPaused = true;
  253. });
  254. },
  255. pause() {
  256. this._audioContext!.onPause(() => {
  257. console.log('音频暂停事件');
  258. this.isPaused = true;
  259. });
  260. this._audioContext!.pause();
  261. this.isPlaying = false;
  262. },
  263. stop() {
  264. console.log('stop');
  265. this._audioContext!.onStop(() => {
  266. // 第一次点停止时,不触发
  267. this.isPaused = true;
  268. console.log('音频停止事件');
  269. });
  270. this._audioContext!.stop();
  271. this.isPlaying = false;
  272. console.log('stop', this.isPaused);
  273. },
  274. destory() {
  275. if (this._audioContext != null) {
  276. this.isPlaying = false;
  277. this._audioContext!.destroy()
  278. }
  279. },
  280. playbackRateChange(e : UniRadioGroupChangeEvent) {
  281. // if (this._audioContext != null && this.isPlaying) {
  282. console.log(parseFloat(e.detail.value))
  283. this._audioContext!.playbackRate = parseFloat(e.detail.value)
  284. // }
  285. },
  286. //just for test
  287. setSrc(src:string){
  288. if( this._audioContext!=null){
  289. this._audioContext!.src = src
  290. }
  291. }
  292. }
  293. }
  294. </script>
  295. <style>
  296. .play-time-area {
  297. display: flex;
  298. flex-direction: row;
  299. margin-top: 20px;
  300. }
  301. .duration {
  302. margin-left: auto;
  303. }
  304. .play-button-area {
  305. display: flex;
  306. flex-direction: row;
  307. justify-content: center;
  308. margin: 50px 0;
  309. }
  310. .icon-play {
  311. width: 60px;
  312. height: 60px;
  313. }
  314. </style>