您好,欢迎访问代理记账网站
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

聊天语音转写的文字记录显示框内,聊天语句随聊天音频播放而滚动,定位并高亮显示

 

1、具体实现如下图:

2、实现功能:

  1. 对话框内的当前播放语句随音频播放而自动滑动到顶部位置;
  2. 亦可前进或后退音频时,对话框定位到音频此时播放语句;
  3. 可以在播放的时候上下滚动对话框,一定的时间内不操作又再次定位到音频正播放的位置;
  4. css区分已播放和未播放的语句,暂停播放音频后,自动滑动停止;

3、代码:

HTML

<div class="IM_box">
          <div class="IM_header">转写记录</div>
          <div class="IM_content" id="IM_content">
            <div :key="item.ownId" v-for="item in conversationList">
              <div class="left_person" v-if="item.roleType == 2">
                <img
                  src="@/assets/images/touxiang_moren.png"
                  class="head_potraite"
                />
                <div style="width: 100%" :id="item.idSelector">
                  <p class="time">{{ getTime(item.beginTime) }}</p>
                  <div
                    class="text_info"
                    :style="{
                      background: item.isPlayed ? '#bab0b0' : '',
                      color: item.isPlayed ? 'white' : '',
                    }"
                  >
                    {{ item.content }}
                  </div>
                </div>
              </div>
              <div class="right_person" v-else>
                <div class="content" :id="item.idSelector">
                  <p class="time" style="text-align: right">
                    {{ getTime(item.beginTime) }}
                  </p>
                  <div
                    class="text_info"
                    :style="{ background: item.isPlayed ? '#1f62a5' : '' }"
                  >
                    {{ item.content }}
                  </div>
                </div>
                <img
                  src="@/assets/images/header_kefu.png"
                  class="head_potraite"
                />
              </div>
            </div>
          </div></div
      >

CSS 

    .IM_box {
      border: 1px solid #eeeeee;
      display: flex;
      flex-direction: column;
      .IM_header {
        width: 100%;
        height: 58px;
        border-bottom: 1px solid #eeeeee;
        line-height: 58px;
        padding-left: 20px;
        font-size: 16px;
        font-family: Microsoft YaHei;
        font-weight: 400;
        color: #333333;
        box-sizing: border-box;
      }
      .IM_content {
        position: relative;
        height: 505px;
        overflow-y: scroll;
        padding: 33px 17px 0px 17px;
        box-sizing: border-box;
        &::-webkit-scrollbar {
          display: none; /* Chrome Safari */
        }
        -ms-overflow-style: none; /* IE 10+ */
        scrollbar-width: none; /* Firefox */
        .time {
          margin-bottom: 5px !important;
        }
        .left_person {
          display: flex;
          margin-bottom: 20px;
          clear: both;
          .head_potraite {
            margin-right: 12px;
          }
          .text_info {
            background: #f9f9f9;
            word-break: break-all;
            color: #333333;
            border: 1px solid #e8e8e8;
            width: max-content;
          }
        }
        .right_person {
          display: flex;
          width: 100%;
          justify-content: flex-end;
          margin-bottom: 20px;
          float: right;
          clear: both;
          .content {
            width: 100%;
            display: flex;
            flex-direction: column;
            align-items: flex-end;
          }
          .head_potraite {
            margin-left: 12px;
          }
          .text_info {
            background: #409efe;
            color: #ffffff;
            word-break: break-all;
          }
        }
        .head_potraite {
          width: 40px;
          height: 40px;
          border-radius: 50%;
        }
        .image_box {
          img {
            max-width: 200px;
            max-height: 320px;
          }
        }
        .file_box {
          background: #f9f9f9;
          border: 1px solid #e8e8e8;
          padding: 10px;
          box-sizing: border-box;
          max-width: 220px;
          border-radius: 6px;
          .file_box_inner {
            display: flex;
            align-items: center;
            margin-bottom: 8px;
            .iconfont {
              font-size: 24px;
              margin-right: 5px;
            }
            .file_name {
              overflow: hidden;
              text-overflow: ellipsis;
              white-space: nowrap;
            }
          }
        }
        .text_info {
          max-width: 60%;
          padding: 14px 13px;
          box-sizing: border-box;
          border-radius: 6px;
          font-size: 14px;
          font-family: Microsoft YaHei;
          font-weight: 400;
        }
      }
    }

JavaScript 

 // 监听音频播放
    subscribeAudio() {
      var audioPlayer = document.getElementById("player");
      let _this = this;
      let justOne = null;
      audioPlayer.addEventListener("timeupdate", function () {
        let curTime = Math.floor(this.currentTime); //监听audio时间变化事件,this.currentTime其中一个秒数的会监听到4次
        //justOne用来控制一个秒数的只执行一次筛选任务
        if (justOne != curTime) {
          // console.log(curTime, "当前秒时");
          justOne = curTime;
          // secStamp是当前对话的秒时,_this.conversationList为对话列表,后台返的数据
          let obj = _this.conversationList.filter(
            (xtem) => xtem.secStamp == curTime
          );
          _this.conversationList.forEach((item) => {
            if (item.secStamp <= curTime) {
              _this.$set(item, "isPlayed", true);
            } else {
              _this.$set(item, "isPlayed", false);
            }
          });
          // console.log("对象值", obj);
          // console.log("数组", _this.conversationList);
          if (obj.length > 0) {
            obj.forEach((x) => {
              _this.slideConversation(x.idSelector);
            });
          }
        }
      });
    },
    // 滑动聊天记录
    slideConversation(sessionItemId) {
      let node = document.querySelector(`#${sessionItemId}`);
      this.IM_content.scroll({ top: node.offsetTop, behavior: "smooth" });
    },

this.conversationList数据结构

 

数据处理

 this.conversationList = res.data.itemList
            .filter((item) => item.content)
            .map((x) => {
              return Object.assign(x, {
                idSelector: `selector${x.sessionItemId}`,
                secStamp: this.msTosec(x.beginTime),
              });
            });

// 毫秒转秒
    msTosec(ms) {
      return Math.trunc(ms / 1000);
    },

 


分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进