瀏覽代碼

音频播放器改造

Michael Wang 5 年之前
父節點
當前提交
b759dbb6e7
共有 2 個文件被更改,包括 120 次插入103 次删除
  1. 108 61
      src/features/OnlineExam/Examing/QuestionAudio.vue
  2. 12 42
      src/features/OnlineExam/Examing/QuestionBody.vue

+ 108 - 61
src/features/OnlineExam/Examing/QuestionAudio.vue

@@ -1,16 +1,25 @@
 <template>
 <template>
-  <span>
-    <audio
-      v-show="shouldShowAudio"
-      :key="src"
-      controls
-      preload="auto"
-      controlsList="nodownload"
-      :name="name"
-      :src="src"
-      style="width: 290px"
-      v-on="$listeners"
-    ></audio>
+  <span style="display: flex; align-items: center; ">
+    <span v-show="shouldShowAudio" @click="play">
+      <Button
+        v-if="!playing"
+        type="primary"
+        shape="circle"
+        icon="ios-play"
+        :disabled="playCount === 0"
+      ></Button>
+      <Button
+        v-else
+        type="primary"
+        shape="circle"
+        icon="md-pause"
+        style="cursor: not-allowed;"
+      ></Button>
+    </span>
+    &nbsp;
+    <span v-show="shouldShowAudio">
+      {{ formatTime(currentTime) }} / {{ formatTime(duration) }}
+    </span>
 
 
     <span v-if="!shouldShowAudio" style="color: blueviolet;">
     <span v-if="!shouldShowAudio" style="color: blueviolet;">
       音频下载中{{ downloadPercent }}%
       音频下载中{{ downloadPercent }}%
@@ -19,6 +28,8 @@
 </template>
 </template>
 
 
 <script>
 <script>
+import moment from "moment";
+
 export default {
 export default {
   name: "QuestionAudio",
   name: "QuestionAudio",
   props: {
   props: {
@@ -30,70 +41,106 @@ export default {
       type: String,
       type: String,
       required: true,
       required: true,
     },
     },
+    playCount: {
+      type: Number,
+      required: true,
+    },
   },
   },
   data() {
   data() {
     return {
     return {
       done: false,
       done: false,
       downloadPercent: 0,
       downloadPercent: 0,
+      currentTime: 0,
+      duration: 0,
+      playing: false,
+      context: null,
+      buffer: null,
     };
     };
   },
   },
   computed: {
   computed: {
     shouldShowAudio() {
     shouldShowAudio() {
-      let chromeVersion = window.navigator.userAgent
-        .split(" ")
-        .find(v => v.startsWith("Chrome/"));
-      if (chromeVersion) {
-        chromeVersion = +chromeVersion.replace("Chrome/", "").split(".")[0];
-      }
-      return (
-        chromeVersion < 76 ||
-        this.done ||
-        !navigator.serviceWorker ||
-        !navigator.serviceWorker.controller ||
-        // 在某些情况下,caches 没有定义,这时候不做cache. 暂时注释
-        // !window.caches ||
-        this.downloadPercent === 100
-      );
+      return this.downloadPercent === 100;
     },
     },
   },
   },
   async created() {
   async created() {
-    this.cacheInterval = setInterval(() => {
-      if (!this.done && window.caches) {
-        window.caches
-          .open("audios")
-          .then(caches => caches.match(this.src))
-          .then(e => e && (this.done = true));
-      }
-    }, 100);
+    this.loadData();
+  },
+  beforeDestroy() {
+    this.source && this.source.stop();
+  },
+  methods: {
+    formatTime(seconds) {
+      return moment.utc(seconds * 1000).format("mm:ss");
+    },
+    loadData() {
+      this.context = new AudioContext();
+      this.buffer;
 
 
-    this.handleMSG = event => {
-      // console.log("Client 1 Received Message: " + event.data);
-      if (event.data[0] === this.src)
-        this.downloadPercent = Math.floor(
-          (event.data[1] / event.data[2]) * 100
-        );
-    };
-    navigator.serviceWorker.addEventListener("message", this.handleMSG);
+      const loadDogSound = url => {
+        var request = new XMLHttpRequest();
+        request.open("GET", url, true);
+        request.responseType = "arraybuffer";
 
 
-    let chromeVersion = window.navigator.userAgent
-      .split(" ")
-      .find(v => v.startsWith("Chrome/"));
-    if (chromeVersion) {
-      chromeVersion = +chromeVersion.replace("Chrome/", "").split(".")[0];
-    }
-    if (chromeVersion >= 76) {
-      if (!window.caches) {
-        window._hmt.push([
-          "_trackEvent",
-          "音频播放",
-          "chromeVersion >= 76 && window.caches 为空",
-        ]);
+        // Decode asynchronously
+        request.onload = () => {
+          this.context.decodeAudioData(
+            request.response,
+            buffer => {
+              this.buffer = buffer;
+              this.duration = Number(this.buffer.duration).toFixed(0);
+              this.downloadPercent = 100;
+              // console.log("load ... ");
+            },
+            () => {
+              console.log("error load audio");
+            }
+          );
+        };
+        request.onprogress = e => {
+          // console.log(e);
+          // console.log(e.loaded / e.total);
+          this.downloadPercent = Number((e.loaded / e.total) * 100).toFixed(2);
+        };
+        request.send();
+      };
+
+      loadDogSound(this.src);
+    },
+    play() {
+      if (this.playCount <= 0) {
+        console.log("无播放次数");
+        return;
       }
       }
-    }
-  },
-  beforeDestroy() {
-    clearInterval(this.cacheInterval);
-    navigator.serviceWorker.removeEventListener("message", this.handleMSG);
+      if (this.playing) {
+        console.log("正在播放,无法暂停");
+        return;
+      }
+      this.$emit("played");
+      // var audio = new Audio();
+      // this.source = this.context.createMediaElementSource(audio); // creates a sound source
+      this.source = this.context.createBufferSource(); // creates a sound source
+      this.source.buffer = this.buffer; // tell the source which sound to play
+      this.source.connect(this.context.destination); // connect the source to the context's destination (the speakers)
+      this.source.start(0); // play the source now
+      // audio.play();
+      this.playing = true;
+      const progress = () => {
+        // console.log(context.currentTime, buffer.duration);
+        this.currentTime = Number(this.context.currentTime).toFixed(0);
+        if (this.context.currentTime < this.duration) {
+          requestAnimationFrame(progress);
+        } else if (this.context.currentTime > this.duration) {
+          this.currentTime = 0;
+        }
+      };
+      requestAnimationFrame(progress);
+      // note: on older systems, may have to use deprecated noteOn(time);
+      this.source.onended = () => {
+        // console.log(e, "ended");
+        this.playing = false;
+        // this.$emit("ended", this.name);
+      };
+    },
   },
   },
 };
 };
 </script>
 </script>

+ 12 - 42
src/features/OnlineExam/Examing/QuestionBody.vue

@@ -7,23 +7,23 @@
     <div v-html="questionDetail.text"></div>
     <div v-html="questionDetail.text"></div>
     <!-- <div v-html="questionDetail.audio"></div> -->
     <!-- <div v-html="questionDetail.audio"></div> -->
     <div
     <div
-      v-for="({ src, name }, index) in questionDetail.audio"
+      v-for="{ src, name } in questionDetail.audio"
       :key="name"
       :key="name"
       class="audio-div"
       class="audio-div"
     >
     >
-      <div style="position: relative;" class="audio-div">
+      <div style="position: relative; margin-bottom: 2px;" class="audio-div">
         <QuestionAudio
         <QuestionAudio
           key="src"
           key="src"
           :name="name"
           :name="name"
           :src="src"
           :src="src"
-          @play="$event => played(index, $event)"
-          @ended="() => audioEnded(name)"
-          @click="$event => audioClicked(index, $event)"
+          :play-count="
+            examQuestion.limitedPlayTimes ? getAudioPlayedTimes(name) : 1
+          "
+          @played="played(name)"
         />
         />
         <span v-if="examQuestion.limitedPlayTimes">
         <span v-if="examQuestion.limitedPlayTimes">
           (剩余播放次数:{{ getAudioPlayedTimes(name) }})
           (剩余播放次数:{{ getAudioPlayedTimes(name) }})
         </span>
         </span>
-        <br />
         <div
         <div
           v-if="audioInPlay.has(name)"
           v-if="audioInPlay.has(name)"
           style="position: absolute;top: 0;right: 0;bottom: 0;left: 0;"
           style="position: absolute;top: 0;right: 0;bottom: 0;left: 0;"
@@ -101,45 +101,15 @@ export default {
       }
       }
       this.questionDetail = question;
       this.questionDetail = question;
     },
     },
-    audioClicked(index, $event) {
-      if ($event.target.paused === false) {
-        // 正在播放中,不能暂停
-        $event.preventDefault();
-      }
-    },
-    audioEnded(name) {
-      const s = new Set([...this.audioInPlay]);
-      s.delete(name);
-      this.audioInPlay = s;
-    },
-    played(index, $event) {
-      const name = $event.target.attributes.name.value;
+    played(name) {
       if (!this.examQuestion.limitedPlayTimes) {
       if (!this.examQuestion.limitedPlayTimes) {
-        const s = new Set([...this.audioInPlay]);
-        s.add(name);
-        this.audioInPlay = s;
-        // 如果没有设置音频播放次数
+        // 如果没有设置音频播放次数,可无限播放
         return false;
         return false;
       }
       }
-      const limitedPlayTimes = this.examQuestion.limitedPlayTimes;
       console.log("开始播放");
       console.log("开始播放");
 
 
-      // console.log(name);
-      const theAudio = this.allAudioPlayTimes.find(a => a.name === name);
-      const playedTimes = (theAudio || { times: 0 }).times;
-      if (limitedPlayTimes - playedTimes <= 0) {
-        console.log("无剩余播放次数");
-        $event.target.pause();
-        return;
-      }
-      const s = new Set([...this.audioInPlay]);
-      s.add(name);
-      this.audioInPlay = s;
-
-      if ($event.target.paused === false) {
-        // 正在播放中
-        this.updateQuestionAudioPlayTimes(name);
-      }
+      // 正在播放中
+      this.updateQuestionAudioPlayTimes(name);
     },
     },
     getAudioPlayedTimes(name) {
     getAudioPlayedTimes(name) {
       return Math.max(
       return Math.max(
@@ -158,8 +128,8 @@ export default {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
 }
 }
-.question-body audio::-webkit-media-controls-timeline,
+/* .question-body audio::-webkit-media-controls-timeline,
 .question-body audio::-webkit-media-controls-timeline-container {
 .question-body audio::-webkit-media-controls-timeline-container {
   display: none;
   display: none;
-}
+} */
 </style>
 </style>