Vue中集成文字转语音:使用Web Speech API实现功能

实现思路

我将创建一个Vue组件,使用浏览器原生的Web Speech API实现文字转语音功能。这个组件将包含文本输入区域、语音控制选项(语速、音调、音量、声音选择)以及播放控制按钮。

功能概览

代码实现

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue文字转语音功能</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
            background: white;
            border-radius: 10px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            padding: 30px;
        }
        h1 {
            text-align: center;
            margin-bottom: 20px;
            color: #2c3e50;
        }
        .description {
            text-align: center;
            margin-bottom: 30px;
            color: #7f8c8d;
        }
        .input-section {
            margin-bottom: 25px;
        }
        textarea {
            width: 100%;
            min-height: 120px;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 5px;
            font-size: 16px;
            resize: vertical;
            transition: border-color 0.3s;
        }
        textarea:focus {
            outline: none;
            border-color: #3498db;
            box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
        }
        .controls {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin-bottom: 25px;
        }
        .control-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: 500;
            color: #2c3e50;
        }
        input[type="range"] {
            width: 100%;
        }
        select {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 5px;
            background: white;
        }
        .button-group {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }
        button {
            padding: 12px 20px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 16px;
            font-weight: 500;
            transition: all 0.3s;
        }
        .btn-play {
            background: #2ecc71;
            color: white;
        }
        .btn-play:hover {
            background: #27ae60;
        }
        .btn-pause {
            background: #f39c12;
            color: white;
        }
        .btn-pause:hover {
            background: #d35400;
        }
        .btn-resume {
            background: #3498db;
            color: white;
        }
        .btn-resume:hover {
            background: #2980b9;
        }
        .btn-stop {
            background: #e74c3c;
            color: white;
        }
        .btn-stop:hover {
            background: #c0392b;
        }
        button:disabled {
            background: #95a5a6;
            cursor: not-allowed;
        }
        .status {
            margin-top: 20px;
            padding: 15px;
            border-radius: 5px;
            background: #f8f9fa;
            text-align: center;
        }
        .speaking {
            background: rgba(46, 204, 113, 0.1);
            color: #27ae60;
        }
        .paused {
            background: rgba(243, 156, 18, 0.1);
            color: #d35400;
        }
        .stopped {
            background: rgba(231, 76, 60, 0.1);
            color: #c0392b;
        }
        .value-display {
            font-size: 14px;
            color: #7f8c8d;
            text-align: right;
        }
        footer {
            margin-top: 30px;
            text-align: center;
            color: #7f8c8d;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <div id="app" class="container">
        <h1>Vue文字转语音功能</h1>
        <p class="description">使用Web Speech API将文本转换为自然语音</p>
        
        <div class="input-section">
            <textarea v-model="text" placeholder="请输入要转换为语音的文字..."></textarea>
        </div>
        
        <div class="controls">
            <div class="control-group">
                <label for="rate">语速: {{ rate }}</label>
                <input type="range" id="rate" min="0.5" max="2" step="0.1" v-model="rate">
                <div class="value-display">慢 ← → 快</div>
            </div>
            
            <div class="control-group">
                <label for="pitch">音调: {{ pitch }}</label>
                <input type="range" id="pitch" min="0.5" max="2" step="0.1" v-model="pitch">
                <div class="value-display">低 ← → 高</div>
            </div>
            
            <div class="control-group">
                <label for="volume">音量: {{ volume }}</label>
                <input type="range" id="volume" min="0" max="1" step="0.1" v-model="volume">
                <div class="value-display">小 ← → 大</div>
            </div>
            
            <div class="control-group">
                <label for="voice">声音</label>
                <select id="voice" v-model="selectedVoice">
                    <option v-for="(voice, index) in voices" :value="index">
                        {{ voice.name }} ({{ voice.lang }})
                    </option>
                </select>
            </div>
        </div>
        
        <div class="button-group">
            <button class="btn-play" @click="speak" :disabled="isSpeaking || !text">播放</button>
            <button class="btn-pause" @click="pause" :disabled="!isSpeaking || isPaused">暂停</button>
            <button class="btn-resume" @click="resume" :disabled="!isPaused">继续</button>
            <button class="btn-stop" @click="stop" :disabled="!isSpeaking && !isPaused">停止</button>
        </div>
        
        <div class="status" :class="statusClass">
            {{ statusMessage }}
        </div>
        
        <footer>
            <p>基于Web Speech API的语音合成功能 | 浏览器兼容性: Chrome, Edge, Safari</p>
        </footer>
    </div>

    <script>
        new Vue({
            el: '#app',
            data: {
                text: '欢迎使用文字转语音功能!这是一个基于Vue和Web Speech API的示例。您可以输入任何文字,然后点击播放按钮进行语音合成。',
                rate: 1,
                pitch: 1,
                volume: 1,
                voices: [],
                selectedVoice: 0,
                isSpeaking: false,
                isPaused: false,
                synthesis: null
            },
            computed: {
                statusMessage() {
                    if (this.isSpeaking && !this.isPaused) {
                        return '正在播放语音...';
                    } else if (this.isPaused) {
                        return '语音已暂停';
                    } else {
                        return '准备就绪';
                    }
                },
                statusClass() {
                    if (this.isSpeaking && !this.isPaused) {
                        return 'speaking';
                    } else if (this.isPaused) {
                        return 'paused';
                    } else {
                        return 'stopped';
                    }
                }
            },
            mounted() {
                // 初始化语音合成
                this.initSpeechSynthesis();
            },
            methods: {
                initSpeechSynthesis() {
                    if ('speechSynthesis' in window) {
                        this.synthesis = window.speechSynthesis;
                        
                        // 获取可用的语音列表
                        const loadVoices = () => {
                            this.voices = this.synthesis.getVoices();
                            // 尝试选择中文语音
                            const chineseVoice = this.voices.find(voice => 
                                voice.lang.includes('zh') || voice.lang.includes('CN'));
                            if (chineseVoice) {
                                this.selectedVoice = this.voices.indexOf(chineseVoice);
                            }
                        };
                        
                        // 语音列表可能异步加载
                        if (this.synthesis.getVoices().length) {
                            loadVoices();
                        } else {
                            this.synthesis.addEventListener('voiceschanged', loadVoices);
                        }
                    } else {
                        alert('很抱歉,您的浏览器不支持语音合成功能!请使用Chrome、Edge或Safari浏览器。');
                    }
                },
                speak() {
                    if (!this.text) return;
                    
                    // 停止当前正在播放的语音
                    this.stop();
                    
                    // 创建新的语音实例
                    const utterance = new SpeechSynthesisUtterance(this.text);
                    utterance.rate = this.rate;
                    utterance.pitch = this.pitch;
                    utterance.volume = this.volume;
                    
                    if (this.voices[this.selectedVoice]) {
                        utterance.voice = this.voices[this.selectedVoice];
                    }
                    
                    // 设置事件监听
                    utterance.onstart = () => {
                        this.isSpeaking = true;
                        this.isPaused = false;
                    };
                    
                    utterance.onend = () => {
                        this.isSpeaking = false;
                        this.isPaused = false;
                    };
                    
                    utterance.onerror = (event) => {
                        console.error('语音合成错误:', event);
                        this.isSpeaking = false;
                        this.isPaused = false;
                    };
                    
                    // 开始播放
                    this.synthesis.speak(utterance);
                },
                pause() {
                    if (this.synthesis && this.isSpeaking) {
                        this.synthesis.pause();
                        this.isPaused = true;
                    }
                },
                resume() {
                    if (this.synthesis && this.isPaused) {
                        this.synthesis.resume();
                        this.isPaused = false;
                    }
                },
                stop() {
                    if (this.synthesis) {
                        this.synthesis.cancel();
                        this.isSpeaking = false;
                        this.isPaused = false;
                    }
                }
            }
        });
    </script>
</body>
</html>

功能说明

  1. 文本输入:用户可以在文本框中输入或粘贴需要转换为语音的文字内容

  2. 语音控制

    • 语速:控制语音播放速度(0.5-2.0)
    • 音调:控制语音的音调高低(0.5-2.0)
    • 音量:控制语音的音量大小(0.0-1.0)
    • 声音选择:选择不同的语音合成引擎(根据浏览器和设备不同而有所区别)
  3. 控制按钮

    • 播放:开始语音合成
    • 暂停:暂停当前语音
    • 继续:从暂停处继续播放
    • 停止:完全停止语音播放
  4. 状态显示:显示当前语音播放状态

技术要点

  • 使用Web Speech API中的SpeechSynthesis接口
  • 创建SpeechSynthesisUtterance对象来管理语音内容
  • 通过Vue数据绑定实现界面与语音参数的实时同步
  • 处理语音合成相关事件(开始、结束、错误)

注意事项

  • 该功能需要浏览器支持Web Speech API(现代浏览器大多支持)
  • 不同浏览器和设备提供的语音合成引擎可能有所不同
  • 某些浏览器可能需要用户交互(如点击)后才能播放语音

您可以直接将上述代码复制到HTML文件中运行,体验文字转语音功能。

参考链接

developer.mozilla.org/en-US/docs/...

相关推荐
李重楼1 小时前
前端性能优化之 HTTP/2 多路复用
前端·面试
yanessa_yu1 小时前
全屏滚动网站PC端自适应方案
前端
RoyLin1 小时前
TypeScript设计模式:桥接模式
前端·后端·typescript
火星开发者1 小时前
Vue中实现Word、Excel、PDF预览的详细步骤
前端
brzhang2 小时前
干翻 Docker?WebAssembly 3.0 的野心,远不止浏览器,来一起看看吧
前端·后端·架构
lecepin3 小时前
AI Coding 资讯 2025-09-17
前端·javascript·面试
IT_陈寒3 小时前
React 18实战:7个被低估的Hooks技巧让你的开发效率提升50%
前端·人工智能·后端
树上有只程序猿3 小时前
终于有人把数据库讲明白了
前端
猩兵哥哥3 小时前
前端面向对象设计原则运用 - 策略模式
前端·javascript·vue.js