self-audio.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <template>
  2. <!--
  3. 引用方式
  4. onLoad() {
  5. var audioPlayer = this.$refs.audioPlayer;
  6. audioPlayer.setSrc(
  7. "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-hello-uniapp/2cc220e0-c27a-11ea-9dfb-6da8e309e0d8.mp3");
  8. // audioPlayer.setPoster(
  9. // "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-uni-app-doc/7fbf26a0-4f4a-11eb-b680-7980c8a877b8.png"); //海报图片
  10. audioPlayer.setSinger("贝多芬"); //设置歌手
  11. audioPlayer.setName("致爱丽丝"); //设置作品名字
  12. //
  13. },
  14. -->
  15. <view class="imt-audio">
  16. <view class="audio-top">
  17. <view class="audio-left">
  18. <view class="audio-control-wrapper">
  19. <image :src="poster" mode="aspectFill" class="cover"></image>
  20. <!-- loading存在逻辑问题,注释掉不用了 -->
  21. <!-- <image :src="require('./static/loading.png')" v-if="playState=='loading'" class="play loading"></image> -->
  22. <!-- <template v-else> -->
  23. <image :src="require('./static/playbtn.png')" alt="play" @click="play" class="play" v-if="playState=='pause'">
  24. </image>
  25. <image :src="require('./static/pausebtn.png')" alt="pause" @click="pause" class="play" v-else></image>
  26. <!-- </template> -->
  27. </view>
  28. </view>
  29. <view class="audio-right">
  30. <view class="audio-wrapper">
  31. <view class="titlebox">
  32. <view class="title">{{name}}</view>
  33. <view v-if="singer" class="singer">{{singer}}</view>
  34. </view>
  35. </view>
  36. <slider class="audio-slider" block-size="12" block-color="#49679c" activeColor="#49679c"
  37. backgroundColor="#dddddd" :max="duration" :value="currentTime" @change="sliderChange"
  38. @changing="sliderChanging"></slider>
  39. <view class="slidebox">
  40. <view>{{formatSeconds(currentTime)}}</view>
  41. <view>{{formatSeconds(duration)}}</view>
  42. </view>
  43. </view>
  44. </view>
  45. <view v-if="playState=='playing'" class="play-btns">
  46. <view @click.stop="sliderTo(-15)">
  47. <u-icon name="rewind-left" color="#898989" size="22"></u-icon>
  48. </view>
  49. <view class="play-speed" @click.stop="changePlaySpeed">
  50. {{currentSpeed==1?'倍数': currentSpeed + ' 倍'}}
  51. </view>
  52. <view @click.stop="sliderTo(15)">
  53. <u-icon name="rewind-right" color="#898989" size="22"></u-icon>
  54. </view>
  55. </view>
  56. </view>
  57. </template>
  58. <script>
  59. /*
  60. createInnerAudioContext()是audio组件的内部实现,不能熄屏播放、不能后台播放、不能倍速播放。
  61. getBackgroundAudioManager() 可以熄屏播放、后台播放,不能倍速播放。缺点是响应速度很慢,无法实现精细、及时的进度控制,而且可能被别的程序占用。
  62. 因此这里只能用video来实现,video能倍速播放,不能熄屏播放、不能后台播放。而且避免了用createInnerAudioContext()实现的跳转到别的页面,还在播放的问题
  63. 因此应用程序可以在需要后台播放的时候(需要用户操作触发),再暂停这个控件的播放,然后自己用getBackgroundAudioManager实现后台播放
  64. */
  65. import Vue from 'vue';
  66. export default {
  67. props: {},
  68. data() {
  69. return {
  70. bgAudio: null,
  71. //
  72. src: "",
  73. poster: "",
  74. name: "...",
  75. singer: "...",
  76. duration: 0,
  77. currentTime: 0,
  78. nextTime: null,
  79. playState: "pause", //"loading"/"playing"/"pause"
  80. isSliderChanging: false,
  81. currentSpeed: 1
  82. };
  83. },
  84. created: function() {
  85. },
  86. methods: {
  87. // 后台播放相关
  88. operateBgAudio(opearteType) {
  89. const that = this;
  90. let bgAudio = this.bgAudio;
  91. if (!bgAudio) {
  92. // 进度条恢复可动
  93. this.isSliderChanging = false;
  94. //
  95. bgAudio = uni.getBackgroundAudioManager();
  96. bgAudio.paused = true;
  97. bgAudio.currentTime = this.currentTime || 0;
  98. bgAudio.title = "致爱丽丝"; //设置作品名字
  99. bgAudio.src = 'https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-hello-uniapp/2cc220e0-c27a-11ea-9dfb-6da8e309e0d8.mp3';
  100. bgAudio.onPlay(e => {
  101. this.playState = "playing";
  102. })
  103. bgAudio.onPause(e => {
  104. this.isSliderChanging = false;
  105. this.playState = "pause";
  106. })
  107. bgAudio.onStop(e => {
  108. // 清空
  109. that.stop();
  110. })
  111. bgAudio.onEnded(e => {
  112. // 清空
  113. that.bgAudio = '';
  114. this.isSliderChanging = false;
  115. that.stop();
  116. })
  117. bgAudio.onTimeUpdate(e => {
  118. if (that.nextTime === 0 || that.nextTime > 0) {
  119. that.currentTime = that.nextTime;
  120. setTimeout(function() {
  121. that.nextTime = null;
  122. that.isSliderChanging = false;
  123. })
  124. } else {
  125. if (that.isSliderChanging == false) {
  126. that.currentTime = that.bgAudio.currentTime || 0;
  127. that.duration = that.bgAudio.duration || 0;
  128. }
  129. }
  130. })
  131. this.bgAudio = bgAudio;
  132. }
  133. switch (opearteType) {
  134. case 'play':
  135. bgAudio.play();
  136. break;
  137. case 'pause':
  138. bgAudio.pause();
  139. break;
  140. case 'sliderChange':
  141. bgAudio.seek(this.nextTime)
  142. this.isSliderChanging = false;
  143. break;
  144. case 'changePlaySpeed':
  145. bgAudio.playbackRate = this.currentSpeed;
  146. break;
  147. }
  148. },
  149. // 切换播放速度
  150. changePlaySpeed() {
  151. if (this.currentSpeed < 2) {
  152. this.currentSpeed += 0.25;
  153. } else {
  154. this.currentSpeed = 1;
  155. }
  156. //
  157. this.operateBgAudio('changePlaySpeed');
  158. },
  159. setSrc: function(value) {
  160. this.src = value;
  161. },
  162. setPoster: function(value) {
  163. this.poster = value;
  164. },
  165. setName: function(value) {
  166. this.name = value;
  167. },
  168. setSinger: function(value) {
  169. this.singer = value;
  170. },
  171. formatSeconds: function(seconds) {
  172. var result = typeof seconds === "string" ? parseFloat(seconds) : seconds;
  173. if (isNaN(result))
  174. return "";
  175. let h = Math.floor(result / 3600) < 10 ?
  176. "0" + Math.floor(result / 3600) :
  177. Math.floor(result / 3600);
  178. let m = Math.floor((result / 60) % 60) < 10 ?
  179. "0" + Math.floor((result / 60) % 60) :
  180. Math.floor((result / 60) % 60) + h * 60;
  181. let s = Math.floor(result % 60) < 10 ?
  182. "0" + Math.floor(result % 60) :
  183. Math.floor(result % 60);
  184. return `${m}:${s}`;
  185. },
  186. play: function() {
  187. this.playState = "playing";
  188. //
  189. this.operateBgAudio("play");
  190. },
  191. pause: function() {
  192. this.playState = "pause";
  193. this.operateBgAudio("pause");
  194. },
  195. sliderTo(time) {
  196. this.isSliderChanging = true;
  197. this.currentTime += time;
  198. this.nextTime = this.currentTime;
  199. this.operateBgAudio("sliderChange");
  200. },
  201. sliderChange: function(e) {
  202. this.isSliderChanging = true;
  203. // 进度条下次变化时间
  204. this.nextTime = e.detail.value;
  205. this.operateBgAudio("sliderChange");
  206. },
  207. sliderChanging: function() {
  208. this.isSliderChanging = true;
  209. },
  210. stop: function() {
  211. this.isSliderChanging = true;
  212. this.currentTime = 0;
  213. this.playState = "pause";
  214. },
  215. },
  216. }
  217. </script>
  218. <style lang="scss">
  219. @import './index.scss';
  220. </style>