动态

详情 返回 返回

前端wavesurfer.js錄音+波形功能 - 动态 详情

vue3項目,主要通過wavesurfer.js實現前端錄音+實時波形渲染

<template>
  <div class="audio-container" @click="onStop">
    <div class="flex-between audio-box" v-if="!transform">
      <div>你可以開始説話</div>
      <div class="audio" id="waveform"></div>
      <StopCircleFilledIcon size="24px" class="active-color" />
    </div>
    <div class="flex-between audio-box" v-else>
      <t-space align="center" class="active-color">
        <LoadIcon size="24px" class="load-icon" />
        正在轉換為文本...
      </t-space>
      <CloseCircleFilledIcon size="24px" class="active-color" />
    </div>
  </div>
</template>

<script setup lang="ts">
  import { StopCircleFilledIcon, CloseCircleFilledIcon, LoadIcon } from 'tdesign-icons-vue-next';
  import WaveSurfer from 'wavesurfer.js';
  import RecordPlugin from 'wavesurfer.js/dist/plugins/record.esm.js';

  // 配置項
  const waveOptions = ref({
    waveColor: '#006EFF',
    progressColor: '#52C41A',
    height: 30, // 波形高度
    barWidth: 2, // 條形寬度
    barGap: 3, // 條形間距
    barRadius: 2, // 條形圓角
    barMinHeight: 0.1, // 波形條的最小高度
    normalize: true, // 是否根據峯值對音頻波形進行縮放
    interact: false, // 開啓鼠標操作
    fillParent: true, // 填充父容器
    pixelRatio: 1, // 加快渲染速度
    minPxPerSec: 50, // 音頻每秒最小像素數
    cursorWidth: 0, // 光標寬度
    responsive: true, // 調整窗口大小時,調整波形大小
    partialRender: true, // 使用PeakCache改善大波形的渲染速度
    removeMediaControl: true,
    hideScrollbar: true // 是否隱藏水平滾動條
  });

  // 是否開啓錄音
  const isAudio = defineModel('isAudio');
  const wavesurfer = ref(); // 實例
  const record = ref(); // 錄音插件
  const init = async () => {
    try {
      await destroy();
      // 實例
      wavesurfer.value = WaveSurfer.create({
        container: '#waveform', // 波形容器
        ...waveOptions.value
      });

      // 創建錄音插件前先檢查權限
      try {
        await navigator.mediaDevices.getUserMedia({ audio: true });
      } catch (error) {
        console.error('獲取麥克風權限失敗:', error);
        return;
      }

      record.value = wavesurfer.value.registerPlugin(
        RecordPlugin.create({
          scrollingWaveform: true,
          renderRecordedAudio: true,
          renderDuringRecording: true,
          mediaRecorderOptions: {
            audioBitsPerSecond: 128000
          }
        })
      );

      record.value.on('record-start', () => {
        console.log('錄音開始');
      });

      record.value.on('record-progress', (time: any) => {
        console.log('更新進度條', time);
      });
    } catch (error) {
      console.error('創建錄音實例失敗:', error, isAudio.value);
    }
  };

  // 停止
  const transform = ref(false);
  const onStop = () => {
    transform.value = !transform.value;
    isAudio.value = false;
  };

  // 實例銷燬
  const destroy = async () => {
    if (wavesurfer.value) {
      wavesurfer.value.destroy();
      wavesurfer.value = null;
    }
  };

  watch(
    isAudio,
    async val => {
      if (val) {
        console.log('開啓錄音');
        await init();
        await record.value.startRecording();
      } else {
        console.log('關閉錄音');
        if (record.value) {
          record.value.stopRecording();
        }
        destroy();
      }
    },
    { immediate: true }
  );
</script>

<style lang="scss" scoped>
  .audio-container {
    .audio-box {
      box-sizing: border-box;
      column-gap: 20px;
    height: 56px;
    padding: 0 10px;
    font-size: $zl-size-1;
    color: $zl-text-color-2;
    border-radius: 6px;
    .audio {
      flex: 1;
    }
    .active-color {
      color: #006eff;
    }
    .load-icon {
      animation: rotate 1s linear infinite;
    }

    @keyframes rotate {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }
  }
}
</style>

Add a new 评论

Some HTML is okay.