AudioPlay.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <template>
  2. <view class="audio-wrapper">
  3. <view :class="[file_url ? 'audio-play' : 'audio-play not-url']" :style="{
  4. padding: showSlider ? '0px 32rpx' : '',
  5. width: showSlider ? 'auto' : '',
  6. justifyContent: showSlider && !showProgress ? 'space-between' : 'center',
  7. backgroundColor: backgroundColor,
  8. }" @click="playAudio(file_url)">
  9. <SvgIcon v-if="!playing" :size="!playing ? 20 : 14" icon-class="audio" />
  10. <img v-else :src="
  11. themeColor === 'gray'
  12. ? 'static/voice_play_gray.png'
  13. : 'static/voice_play_white.png'
  14. " class="voice-play" />
  15. <template v-if="showSlider&&file_url">
  16. <text class="audio-time" v-if="showProgress"> {{ currentTime }} </text>
  17. <slider v-if="showProgress" class="audio-slider" v-model="sliderValue" activeColor="#ffffff"
  18. backgroundColor="#1853C6" block-size="12" @change="onSliderChange" />
  19. <text class="audio-time"> {{ duration }} </text>
  20. </template>
  21. </view>
  22. </view>
  23. </template>
  24. <script>
  25. import {
  26. GetFileURLMap
  27. } from '@/api/api.js';
  28. import {
  29. secondFormatConversion
  30. } from '@/utils/transform';
  31. export default {
  32. name: 'AudioPlay',
  33. props: {
  34. fileId: {
  35. type: String,
  36. required: true,
  37. },
  38. themeColor: {
  39. type: String,
  40. default: '',
  41. },
  42. showSlider: {
  43. type: Boolean,
  44. default: false,
  45. },
  46. // 是否显示音频进度条
  47. showProgress: {
  48. type: Boolean,
  49. default: true,
  50. },
  51. // 播放背景色
  52. backgroundColor: {
  53. type: String,
  54. default: '#165dff',
  55. },
  56. isPlaying: {
  57. type: Boolean,
  58. default: false,
  59. },
  60. questionId: {
  61. type: String,
  62. default: '',
  63. }
  64. },
  65. data() {
  66. return {
  67. secondFormatConversion,
  68. file_url: '',
  69. playing: false,
  70. audio: uni.createInnerAudioContext(),
  71. duration: '00:00', //音频时长
  72. currentTime: '00:00', //当前时长
  73. sliderValue: 0,
  74. };
  75. },
  76. computed: {
  77. // iconClass() {
  78. // return this.audio.paused ? 'audio' : 'audio-stop';
  79. // },
  80. },
  81. watch: {
  82. fileId: {
  83. handler(val) {
  84. var that = this;
  85. if (!val) return;
  86. GetFileURLMap({
  87. file_id_list: [val]
  88. }).then(({
  89. url_map
  90. }) => {
  91. that.file_url = url_map[val];
  92. that.audio.src = url_map[val];
  93. that.playing = false;
  94. that.currentTime = '00:00';
  95. that.sliderValue = 0;
  96. // uni.getSystemInfoSync().platform == 'ios'
  97. that.audio.onCanplay(() => {
  98. if (that.audio.duration !== 0) {
  99. clearInterval(that.intervalID);
  100. that.duration = that.secondFormatConversion(that.audio.duration) // 总时长
  101. that.currentTime = that.secondFormatConversion(that.audio.currentTime) // 当前时长
  102. }
  103. });
  104. that.audio.onTimeUpdate(() => {
  105. that.currentTime = that.secondFormatConversion(that.audio.currentTime);
  106. if (!that.dragging) { // 避免拖动滑块时自动更新
  107. that.sliderValue = (that.audio.currentTime / that.audio.duration) * 100;
  108. }
  109. })
  110. });
  111. },
  112. immediate: true,
  113. },
  114. isPlaying: {
  115. handler(val) {
  116. if (!val) {
  117. this.audio.pause();
  118. this.playing = false;
  119. }
  120. }
  121. },
  122. questionId: {
  123. handler(val) {
  124. this.audio.pause();
  125. this.playing = false;
  126. }
  127. },
  128. },
  129. destroyed() {
  130. this.audio.pause();
  131. this.playing = false;
  132. },
  133. methods: {
  134. playAudio() {
  135. if (!this.file_url) return;
  136. clearInterval(this.timer);
  137. if (this.playing) {
  138. this.audio.pause();
  139. } else {
  140. this.audio.play();
  141. this.timer = setInterval(() => {
  142. if (this.audio.currentTime > 0 && this.audio.currentTime < this.audio.duration) {
  143. this.sliderValue = (this.audio.currentTime / this.audio.duration) * 100;
  144. } else {
  145. this.playing = !this.playing;
  146. clearInterval(this.timer);
  147. }
  148. }, 1000);
  149. }
  150. this.playing = !this.playing;
  151. if (this.playing)
  152. uni.$emit('setOtherAudioPlaying', this.fileId, this.playing);
  153. },
  154. onSliderChange(event) {
  155. const value = event.detail.value;
  156. if (this.audio && this.audio.duration > 0) {
  157. // 验证 value 是否是有效的百分比值(0-100之间)
  158. if (isNaN(value) || value < 0 || value > 100) {
  159. // console.error("Invalid value:", value);
  160. return;
  161. }
  162. // 计算新的位置
  163. const newPosition = (value / 100) * this.audio.duration;
  164. // 验证 newPosition 是否是有效的数字,不是 NaN,并且在有效范围内
  165. if (!isNaN(newPosition) && newPosition >= 0 && newPosition <= this.audio.duration) {
  166. // 设置音频播放位置
  167. this.audio.seek(newPosition);
  168. if (!this.dragging) { // 避免拖动滑块时自动更新
  169. this.sliderValue = value;
  170. }
  171. } else {
  172. console.error("Invalid newPosition:", newPosition);
  173. }
  174. }
  175. },
  176. },
  177. };
  178. </script>
  179. <style lang="scss" scoped>
  180. .audio-wrapper {
  181. margin: 32rpx 0;
  182. .audio-play {
  183. display: flex;
  184. column-gap: 24rpx;
  185. align-items: center;
  186. justify-content: center;
  187. width: 80rpx;
  188. height: 80rpx;
  189. color: #ffffff;
  190. background-color: $uni-color-main;
  191. border-radius: 80rpx;
  192. &.not-url {
  193. color: #a1a1a1;
  194. }
  195. .voice-play {
  196. width: 40rpx;
  197. height: 40rpx;
  198. }
  199. .audio-time {
  200. min-width: 70rpx;
  201. font-size: 24rpx;
  202. font-weight: 400;
  203. }
  204. .audio-slider {
  205. flex: 2;
  206. margin: 0;
  207. }
  208. }
  209. }
  210. </style>