natasha 1 年之前
父节点
当前提交
94cfa4b4b1
共有 3 个文件被更改,包括 190 次插入10 次删除
  1. 1 0
      package.json
  2. 二进制
      src/assets/voice-gif.png
  3. 189 10
      src/views/bookShelf/components/PracticeModel.vue

+ 1 - 0
package.json

@@ -21,6 +21,7 @@
         "element-china-area-data": "^6.0.2",
         "element-ui": "^2.13.2",
         "jquery": "^3.7.1",
+        "js-audio-recorder": "^1.0.7",
         "js-base64": "^3.6.1",
         "js-cookie": "2.2.0",
         "js-md5": "^0.7.3",

二进制
src/assets/voice-gif.png


+ 189 - 10
src/views/bookShelf/components/PracticeModel.vue

@@ -153,7 +153,7 @@
         <div class="operate-box" :style="{background:colorObj.contentBg}">
             <div class="operate-box-inner" :style="{background:colorObj.contentInnerBg}">
                 <div class="operate-box-inner-content">
-                    <div class="operate-item">
+                    <div class="operate-item" @click="changePlaySent('-')">
                         <svg-icon icon-class="Go-start" :style="{color:colorObj.type==='white'||colorObj.type==='darkGreen'?'#000':'#fff'}"></svg-icon>
                         <span :style="{color:colorObj.type==='armyGreen'?'#7C8983':''}">上一句</span>
                     </div>
@@ -162,16 +162,20 @@
                         <span :style="{color:colorObj.type==='armyGreen'?'#7C8983':''}">听对比</span>
                     </div>
                     <div class="operate-item">
-                        <b class="luyin-btn">
+                        <b class="luyin-btn" v-if="!microphoneStatus" @click="microphone">
                             <svg-icon icon-class="Voice-luyin" :style="{color:'#fff'}"></svg-icon>
                         </b>
+                        <img class="luyin-gif" @click="microphone" v-else src="../../../assets/voice-gif.png" />
+                        <span v-if="microphoneStatus" class="record-time" :style="{color:'#F2555A',fontWeight: '600'}">{{
+                            handleDateTime(recordtime)
+                        }}</span>
                     </div>
-                    <div class="operate-item">
+                    <div class="operate-item" @click="playmicrophone(selectIndex || selectIndex == 0 ? recordList[selectIndex].toltime : '')">
                         <svg-icon icon-class="Headphone-sound" :style="{color:colorObj.type==='white'||colorObj.type==='darkGreen'?'#000':'#fff'}"></svg-icon>
                         <span :style="{color:colorObj.type==='armyGreen'?'#7C8983':''}">我读的</span>
                     </div>
-                    <div class="operate-item">
-                        <svg-icon icon-class="Go-start" :style="{color:colorObj.type==='white'||colorObj.type==='darkGreen'?'#000':'#fff'}"></svg-icon>
+                    <div class="operate-item" @click="changePlaySent('+')">
+                        <svg-icon icon-class="Go-end" :style="{color:colorObj.type==='white'||colorObj.type==='darkGreen'?'#000':'#fff'}"></svg-icon>
                         <span :style="{color:colorObj.type==='armyGreen'?'#7C8983':''}">下一句</span>
                     </div>
                 </div>
@@ -218,6 +222,7 @@
 
 <script>
 import AudioLine from "@/components/common/AudioLine.vue"
+import Recorder from "js-audio-recorder"; // 录音插件
 export default {
   name: "ArticleView",
   props: [ "titleFontsize", "wordFontsize", "colorObj","articleType","articleInfo"],
@@ -273,6 +278,24 @@ export default {
       repeatAfter: false,
       singleModel: false,
       autoNextSent: false,
+      recorder: new Recorder({
+        sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
+        sampleRate: 16000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
+        numChannels: 1, // 声道,支持 1 或 2, 默认是1
+      }),
+      microphoneStatus: false,
+      hasMicro: "", // 录音后的样式class
+      wavblob: null,
+      audio: new window.Audio(),
+      recordList: [], // 录音文件数组
+      recordtime: 0, // 录音时长
+      timer: null, // 计时器
+      recordFile: 1, // 录音文件名
+      selectIndex: null, // 选中的录音索引
+      oldIndex: null, // 存储播放录音索引
+      playtime: 0, // 播放时间
+      isPlayings: false,
+      playSentIndex: -1, // 播放的句子索引
     };
   },
   computed: {
@@ -287,8 +310,30 @@ export default {
   watch: {},
   //方法集合
   methods: {
+    changePlaySent(type){
+        for(let i=0; i<this.resArr.timeList.length;i++){
+            if(this.curTime>=this.resArr.timeList[i].s&&this.curTime<this.resArr.timeList[i].e){
+                this.playSentIndex = i
+                break
+            }
+        }
+        if(type==='-'){
+            if(this.playSentIndex>0){
+                this.playSentIndex--
+            }else{
+                this.playSentIndex=0
+            }
+        }else{
+            if(this.playSentIndex<this.resArr.wordsList.length-1){
+                this.playSentIndex++
+            }else{
+                this.playSentIndex=0
+            }
+        }
+        this.handleChangeTime(this.resArr.timeList[this.playSentIndex].s)
+    },
     getCurTime(curTime) {
-      this.curTime = curTime * 1000;
+      this.curTime = curTime * 1000;  
     },
     handleData() {
       let resArr = {
@@ -335,7 +380,133 @@ export default {
         if(this[val]){
             this[val1] = false
         }
-    }
+    },
+    // 开始录音
+    microphone() {
+      let _this = this;
+      if (!this.microphoneStatus) {
+        _this.hasMicro = "";
+        // _this.$emit("getWavblob", null);
+        // this.$emit("getSelectData", { type: "" });
+        // 开始录音
+        this.recorder.start();
+        this.microphoneStatus = true;
+        this.recordtime = 0;
+        this.isPlayings = false;
+        clearInterval(_this.timer);
+        _this.timer = setInterval(() => {
+          _this.recordtime++;
+        }, 1000);
+        this.$emit("handleParentPlay");
+        let obj = {
+          name: _this.fileName
+            ? _this.fileName + _this.recordFile
+            : "Recording " + _this.recordFile,
+          id: _this.recordFile + Math.round(Math.random() * 10),
+        };
+        if (this.selectData) obj.selectData = this.selectData;
+        _this.recordList.push(obj);
+        _this.recordFile++;
+        _this.selectIndex = _this.recordList.length - 1;
+      } else {
+        this.hasMicro = "normal";
+        this.recorder.stop();
+        clearInterval(_this.timer);
+        let toltime = this.recorder.duration; // 录音总时长
+        let fileSize = this.recorder.fileSize; // 录音总大小
+        // 录音结束,获取取录音数据
+        let wav = this.recorder.getWAVBlob(); // 获取 WAV 数据
+        // this.wavblob = wav;
+        this.microphoneStatus = false;
+        let reader = new window.FileReader();
+        reader.readAsDataURL(wav);
+        reader.onloadend = () => {
+          _this.recordList[_this.selectIndex].wavData = reader.result;
+          _this.recordList[_this.selectIndex].toltime = Math.floor(toltime);
+          _this.recordList[_this.selectIndex].fileSize = fileSize;
+
+          _this.wavblob = _this.recordList[_this.selectIndex].wavData;
+          _this.$emit("getWavblob", _this.wavblob);
+          _this.$emit(
+            "handleWav",
+            JSON.parse(JSON.stringify(_this.recordList)),
+            _this.tmIndex
+          );
+          if (this.recordList[this.selectIndex].selectData) {
+            this.$emit(
+              "getSelectData",
+              this.recordList[this.selectIndex].selectData
+            );
+          }
+        };
+      }
+      _this.$emit(
+        "getRerordStatus",
+        !_this.microphoneStatus && _this.recordList.length > 0
+      );
+      _this.$emit("getMicrophoneStatus", _this.microphoneStatus);
+    },
+    playmicrophone(totalTimes) {
+      if (this.hasMicro) {
+        this.isPlayings = true;
+        if (this.selectIndex || this.selectIndex == 0) {
+          let totalTime = totalTimes;
+          let _this = this;
+          if (!this.audio.paused) {
+            this.audio.pause();
+            clearInterval(_this.timer);
+          } else if (this.audio.paused && _this.oldIndex == _this.selectIndex) {
+            _this.audio.play();
+            if (_this.recordtime == 0) {
+              _this.recordtime = totalTimes;
+              _this.playtime = 0;
+            }
+            _this.timer = setInterval(() => {
+              if (_this.playtime < totalTime) {
+                _this.playtime++;
+                _this.recordtime = totalTime - _this.playtime;
+              } else {
+                clearInterval(_this.timer);
+              }
+            }, 1000);
+          } else {
+            _this.audio.pause();
+            _this.audio.load();
+            _this.audio.src = _this.wavblob;
+            _this.oldIndex = _this.selectIndex;
+            _this.audio.play();
+            _this.playtime = 0;
+            _this.recordtime = totalTime;
+            clearInterval(_this.timer);
+            _this.timer = setInterval(() => {
+              if (_this.playtime < totalTime) {
+                _this.playtime++;
+                _this.recordtime = totalTime - _this.playtime;
+              } else {
+                clearInterval(_this.timer);
+              }
+            }, 1000);
+          }
+        }
+      }
+    },
+    // 格式化录音时长
+    handleDateTime(time) {
+      if (parseInt(time / 60) < 10) {
+        time =
+          ("0" + parseInt(time / 60)).substring(
+            ("0" + parseInt(time / 60)).length - 2
+          ) +
+          ":" +
+          ("0" + (time % 60)).substring(("0" + (time % 60)).length - 2);
+      } else {
+        time =
+          parseInt(time / 60) +
+          ":" +
+          ("0" + (time % 60)).substring(("0" + (time % 60)).length - 2);
+      }
+      return time;
+    },
   },
   //生命周期 - 创建完成(可以访问当前this实例)
   created() {},
@@ -484,16 +655,24 @@ export default {
                 display: block;
                 width: 64px;
                 height: 64px;
-                padding: 16px;
+                padding: 10px;
                 border-radius: 60px;
                 background: #F2555A;
                 box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.08), 0px 16px 24px 2px rgba(0, 0, 0, 0.04), 0px 6px 30px 5px rgba(0, 0, 0, 0.05);
             }
+            .luyin-gif{
+               display: block;
+                width: 64px;
+                height: 64px; 
+                border-radius: 60px;
+                box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.08), 0px 16px 24px 2px rgba(0, 0, 0, 0.04), 0px 6px 30px 5px rgba(0, 0, 0, 0.05);
+            }
             .svg-icon{
                 display: block;
-                width: 32px;
-                height: 32px;
+                width: 44px;
+                height: 44px;
                 margin: 0 auto;
+                padding: 6px;
             }
             span{
                 color: #929CA8;