QuestionAudio.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <template>
  2. <span class="a-container" style="display: inline-flex; align-items: center">
  3. <span v-show="shouldShowAudio" @click.stop="play">
  4. <Button
  5. v-if="!playing"
  6. type="primary"
  7. shape="circle"
  8. icon="md-play"
  9. style="padding-left: 5px"
  10. :disabled="playCount === 0"
  11. class="play"
  12. ></Button>
  13. <Button
  14. v-else
  15. type="primary"
  16. shape="circle"
  17. icon="md-pause"
  18. style="cursor: not-allowed"
  19. class="pause"
  20. ></Button>
  21. </span>
  22. <span style="padding-left: 2px">&nbsp;</span>
  23. <span v-show="shouldShowAudio" style="color: red">
  24. {{ formatTime(currentTime) }} / {{ formatTime(duration) }}
  25. </span>
  26. <span v-if="!shouldShowAudio" style="color: blueviolet">
  27. 音频下载中{{ downloadPercent }}%
  28. </span>
  29. </span>
  30. </template>
  31. <script>
  32. import moment from "moment";
  33. export default {
  34. name: "QuestionAudio",
  35. props: {
  36. src: {
  37. type: String,
  38. required: true,
  39. },
  40. name: {
  41. type: String,
  42. required: true,
  43. },
  44. playCount: {
  45. type: Number,
  46. required: true,
  47. },
  48. },
  49. data() {
  50. return {
  51. done: false,
  52. downloadPercent: 0,
  53. currentTime: 0,
  54. duration: 0,
  55. playing: false,
  56. context: null,
  57. buffer: null,
  58. };
  59. },
  60. computed: {
  61. shouldShowAudio() {
  62. return this.downloadPercent === 100;
  63. },
  64. },
  65. async created() {
  66. this.loadData();
  67. },
  68. beforeDestroy() {
  69. this.source && this.source.stop();
  70. if (this.context) {
  71. this.context.close();
  72. this.context = null;
  73. }
  74. },
  75. methods: {
  76. formatTime(seconds) {
  77. return moment.utc(seconds * 1000).format("mm:ss");
  78. },
  79. loadData() {
  80. this.context = new AudioContext();
  81. this.buffer;
  82. const loadDogSound = (url) => {
  83. var request = new XMLHttpRequest();
  84. request.open("GET", url, true);
  85. request.responseType = "arraybuffer";
  86. // Decode asynchronously
  87. request.onload = () => {
  88. this.context.decodeAudioData(
  89. request.response,
  90. (buffer) => {
  91. this.buffer = buffer;
  92. this.duration = Math.floor(this.buffer.duration);
  93. this.downloadPercent = 100;
  94. // console.log("load ... ");
  95. },
  96. () => {
  97. console.log("error load audio");
  98. }
  99. );
  100. };
  101. request.onprogress = (e) => {
  102. // console.log(e);
  103. // console.log(e.loaded / e.total);
  104. this.downloadPercent = Number((e.loaded / e.total) * 100).toFixed(2);
  105. };
  106. request.send();
  107. };
  108. loadDogSound(this.src);
  109. },
  110. async play() {
  111. if (this.playCount <= 0) {
  112. console.log("无播放次数");
  113. return;
  114. }
  115. if (this.playing) {
  116. console.log("正在播放,无法暂停");
  117. return;
  118. }
  119. this.$emit("played");
  120. // var audio = new Audio();
  121. // this.source = this.context.createMediaElementSource(audio); // creates a sound source
  122. // await this.context.decodeAudioData(this.buffer);
  123. this.context.close();
  124. this.context = new AudioContext();
  125. this.source = this.context.createBufferSource(); // creates a sound source
  126. this.source.buffer = this.buffer; // tell the source which sound to play
  127. this.source.connect(this.context.destination); // connect the source to the context's destination (the speakers)
  128. this.source.start(0); // play the source now
  129. // audio.play();
  130. this.playing = true;
  131. this.currentTime = 0;
  132. const progress = () => {
  133. // console.log(context.currentTime, buffer.duration);
  134. this.currentTime = Math.floor(this.context.currentTime);
  135. if (this.context.currentTime < this.buffer.duration) {
  136. requestAnimationFrame(progress);
  137. } else if (this.context.currentTime >= this.buffer.duration) {
  138. this.currentTime = 0;
  139. requestAnimationFrame(progress);
  140. }
  141. };
  142. requestAnimationFrame(progress);
  143. // note: on older systems, may have to use deprecated noteOn(time);
  144. this.source.onended = () => {
  145. // console.log(e, "ended");
  146. this.playing = false;
  147. this.currentTime = 0;
  148. // this.$emit("ended", this.name);
  149. };
  150. },
  151. },
  152. };
  153. </script>
  154. <style scoped>
  155. .a-container >>> .ivu-btn-small {
  156. width: 18px !important;
  157. height: 18px !important;
  158. font-size: 11px !important;
  159. margin-bottom: 4px;
  160. }
  161. .a-container >>> .ivu-btn-small.play {
  162. padding-left: 2px !important;
  163. }
  164. .a-container >>> .ivu-btn-small.pause {
  165. padding-left: 0px !important;
  166. padding-top: 1px !important;
  167. }
  168. </style>