QuestionAudio.vue 4.6 KB

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