QuestionAudio.vue 4.1 KB

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