当前位置: 首页 > news >正文

vue在线录音系统

说明:
用vue做一款录音系统

1.点击按钮,开始录制音频

2.录制过程中,可以暂停和停止录制 有时长显示

3.点击停止录制 可以保存音频,保存在本地

4.找到刚刚保存的音频路径,可以点击播放 ,需要显示音频总时长

5.可以下载音频

效果图:
在这里插入图片描述

step1:

<template>
  <div class="container">
    <h1>录音系统</h1>

    <!-- 录音控制 -->
    <div class="controls">
      <button @click="toggleRecording" :disabled="isPaused">
        {{ isRecording ? '正在录音...' : '开始录音' }}
      </button>
      <button @click="togglePause" :disabled="!isRecording">
        {{ isPaused ? '继续录音' : '暂停录音' }}
      </button>
      <button @click="stopRecording" :disabled="!isRecording && !isPaused">
        停止并保存
      </button>
    </div>

    <!-- 录音时长 -->
    <div class="duration">当前时长: {{ formatTime(currentDuration) }}</div>

    <!-- 保存的录音 -->
    <div class="recordings" v-if="currentRecording">
      <h2>保存的录音</h2>
      <div class="audio-item">
        <audio ref="audioPlayer" controls @loadedmetadata="updateDuration"></audio>
        <div>
          <button @click="playRecording">播放</button>
          <span>时长: {{ formatTime(currentRecording.duration) }}</span>
          <a :href="currentRecording.url" download="recording.webm">下载</a>
          <button @click="deleteRecording" class="delete-btn">删除</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isRecording: false,
      isPaused: false,
      currentDuration: 0,
      timer: null,
      mediaRecorder: null,
      audioChunks: [],
      currentRecording: null
    };
  },
  methods: {
    async toggleRecording() {
      if (!this.isRecording) {
        try {
          const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
          this.mediaRecorder = new MediaRecorder(stream);

          this.mediaRecorder.ondataavailable = event => {
            this.audioChunks.push(event.data);
          };

          this.mediaRecorder.onstop = () => {
            const audioBlob = new Blob(this.audioChunks, { type: 'audio/webm' });
            const audioUrl = URL.createObjectURL(audioBlob);

            // 只保留最新录音
            this.currentRecording = {
              url: audioUrl,
              duration: this.currentDuration
            };

            this.audioChunks = [];
            stream.getTracks().forEach(track => track.stop());
          };

          this.mediaRecorder.start();
          this.startTimer();
          this.isRecording = true;
          this.isPaused = false;
        } catch (err) {
          console.error('无法访问麦克风:', err);
        }
      }
    },

    // 新增删除方法
    deleteRecording() {
      if (this.currentRecording) {
        URL.revokeObjectURL(this.currentRecording.url);
        this.currentRecording = null;
      }
    },

    togglePause() {
      if (this.isPaused) {
        this.mediaRecorder.resume();
        this.startTimer();
      } else {
        this.mediaRecorder.pause();
        clearInterval(this.timer);
      }
      this.isPaused = !this.isPaused;
    },

    stopRecording() {
      this.mediaRecorder.stop();
      clearInterval(this.timer);
      this.isRecording = false;
      this.isPaused = false;
      this.currentDuration = 0;
    },

    startTimer() {
      this.timer = setInterval(() => {
        this.currentDuration++;
      }, 1000);
    },

    playRecording() {
      const audioElement = this.$refs.audioPlayer;
      audioElement.src = this.currentRecording.url;
      audioElement.play();
    },

    updateDuration() {
      const audioElement = this.$refs.audioPlayer;
      if (this.currentRecording) {
        this.currentRecording.duration = Math.round(audioElement.duration);
      }
    },

    formatTime(seconds) {
      const mins = Math.floor(seconds / 60);
      const secs = seconds % 60;
      return `${mins}:${secs.toString().padStart(2, '0')}`;
    }
  },
  beforeUnmount() {
    clearInterval(this.timer);
    if (this.currentRecording) {
      URL.revokeObjectURL(this.currentRecording.url);
    }
  }
};
</script>

<style>
/* 新增删除按钮样式 */
.delete-btn {
  background-color: #ff4444;
  margin-left: 10px;
}

.container {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
}

.controls {
  margin: 20px 0;
  display: flex;
  gap: 10px;
}

.audio-item {
  margin: 10px 0;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.duration {
  font-size: 1.2em;
  margin: 10px 0;
}

button {
  padding: 8px 16px;
  cursor: pointer;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
}

button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

a {
  margin-left: 10px;
  color: #2196F3;
  text-decoration: none;
}

</style>

end

http://www.dtcms.com/a/99415.html

相关文章:

  • Redis延时队列在订单超时未报到场景的应用补充说明
  • 利用 VSCode 配置提升 vibe coding 开发效率
  • 找python大数据就业,我应该学习Java哪些知识
  • dav_pg8_vacuum
  • c#的.Net Framework 的console 项目找不到System.Window.Forms 引用
  • VMware中新建Ubuntu虚拟机系统,并安装Anaconda
  • 1--当「穷举」成为艺术:CTF暴力破解漏洞技术从入门到入刑指南(知识点讲解版)
  • Android Architecture Components 深入解析
  • 【力扣刷题|第十七天】0-1 背包 完全背包
  • Linux进程管理的相关知识点以及我的相关笔记链接
  • 算法为舟 思想为楫:AI时代,创作何为?
  • Redis的ZSet有序集合
  • 深度剖析:U盘突然无法访问的数据拯救之道
  • 27. 移除元素【数组专题】Java\Python\JS\Go\C++
  • IP综合实验
  • 曲线拟合 | Matlab基于贝叶斯多项式的曲线拟合
  • Docker, Docker 镜像是什么,怎么创建, Docker有什么用
  • 微服务架构中的精妙设计:SpringCloud的概述
  • Dust3r、Mast3r、Fast3r
  • 信息学奥赛一本通 1609:【例 4】Cats Transport | 洛谷 CF311B Cats Transport
  • VectorBT:使用PyTorch+LSTM训练和回测股票模型 进阶三
  • 【C++初阶】---类和对象(下)
  • 每日一题之修建灌木
  • 2D视觉系统选型(待补充)
  • DHCP报文的详细流程
  • 高精度十进制数运算
  • Python库()
  • 【人工智能之大模型】哪些因素会导致大语言模型LLM的偏见?
  • 淘宝店铺清单及全商品数据、关键词检索 API 介绍
  • 金融级密码管理器——跨设备同步的端到端加密方案