vue实现文字转语音的组件,window.speechSynthesis,实现H5语音合成,class类封装,实现项目介绍文字播放,不需安装任何包和插件(2024-04-17)

本文介绍了如何使用HTML5的SpeechSynthesisAPI在网页上实现动态文本的语音合成,包括主流的付费与免费方案对比,以及如何配置语音属性和选择合适的语音包,特别关注了GoogleChrome在MacOS下的实现和语音包来源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

语音合成(Text To Speech,TTS)可以实现网页文章的阅读。通过它,我们可以不需要看文字,就可以听新闻、听小说。

目前国内的主流的语音合成解决方案无非是以下几种:

  1. 科大讯飞
  2. Google
  3. 云服务(阿里云、腾讯云……)
  4. 百度api

上述的方案,音质不错,语音包选择多。但是都有一定的免费配额或者语言的限制(如科大讯飞的试用语音包),没有完全免费的方案。

开源免费的方案的效果比较差,而且需要利用自己的服务器资源,需要考虑额外的成本。

那有没有什么免费可以使用的方案呢?还是有的!

这里就要搬出来 Web API 里面的 SpeechSynthesis。这个接口是浏览器原生支持的语音合成。

基本上现在主流的现代浏览器都支持这个接口。具体的浏览器支持情况可以查阅上面链接里的文档。

1、SpeechSynthesis技术简介

SpeechSynthesis是HTML5的一个新特性,基于SpeechSynthesis可以实现在客户浏览器端进行动态文本的语音合成播放。

在HTML5中和Web Speech相关的API实际上有两类,一类是“语音识别(Speech Recognition)”,另外一个就是“语音合成(Speech Synthesis)”,实际上指的分别是“语音转文字”,和“文字变语音”。

文章介绍的就是“语音合成-文字变语音”。为什么称为“合成”呢?比方说你Siri发音“你好,世界!” 实际上是把“你”、“好”、“世”、“界”这4个字的读音给合并在一起,因此,称为“语音合成”。

技术参考api-MDN

mdn英文文档:MDN Web Docs

 mdn中文文档:Web 开发者指南 | MDN

1.1 SpeechSynthesis 的技术类方法

1.2 SpeechSyntehesisUtteranc属性

这个类主要用于控制合成声音的属性配置,比如主要内容,语音模板,语速等等,通过这个核心类控制。它的属性信息如下:

序号

参数

解释

1

text

要合成的文字内容,字符串

2

lang

使用的语言,字符串, 例如:"zh-cn"

3

voiceURI

指定希望使用的声音和服务,字符串。

4

volume

声音的音量,区间范围是0到1,默认是1

5

rate

语速,数值,默认值是1,范围是0.1到10,表示语速的倍数,例如2表示正常语速的两倍。

6

pitch

表示说话的音高,数值,范围从0(最小)到2(最大)。默认值为1

1.3  SpeechSyntehesisUtteranc核心方法

如下表所示:

序号

方法名

说明

1

onstart

语音合成开始时候的回调。

2

onpause

语音合成暂停时候的回调

3

onresume

语音合成重新开始时候的回调

4

onend

语音合成结束时候的回调

5

onmark

Fired when the spoken utterance reaches a named SSML "mark" tag.

1.4 speechSynthesis对象

 是实际调用SpeechSynthesisUtterance对象进行合成播报的。

他的属性和方法如下两个表格描述。

序号

名称

描述

1

paused

当SpeechSynthesis 处于暂停状态时, Boolean (en-US) 值返回 true

2

pending

当语音播放队列到目前为止保持没有说完的语音时, Boolean (en-US) 值返回 true 。

3

speaking

当语音谈话正在进行的时候,即使SpeechSynthesis处于暂停状态, Boolean (en-US) 返回 true 。

1.5 语音包

简单来说,SpeechSynthesis 的语音包来自两大地方——操作系统自带和浏览器自带。

所以我们才发现 window.speechSynthesis.getVoices() 拿到的语音包存在一部分是 Apple 提供的,一部分是 Google 提供的。 

对于语音包的一些技术解释,具体的文档可以查看 MDN。

以 macOS 下的 Google Chrome 为例,
通过 window.speechSynthesis.getVoices() 可以获取全部语音包,下面是一部分的输出:

[{
  "voiceURI": "Ting-Ting",
  "name": "Ting-Ting",
  "lang": "zh-CN",
  "localService": true,
  "default": true
}, {
  "voiceURI": "Google 普通话(中国大陆)",
  "name": "Google 普通话(中国大陆)",
  "lang": "zh-CN",
  "localService": false,
  "default": false
}]
上述数组是 SpeechSynthesisVoice 的实例数组,SpeechSynthesisVoice 的参数如下:

voiceURI:语音合成服务的URL
name:语音包名称
lang:语言(符合BCP 47规范)
localService:是本地语言包(离线)还是远程语言包(网络)
default:是否为默认的语音包

通过观察,可以发现
Google Chrome 在 macOS 下可以使用的语音包有macOS 自带的本地语音包和谷歌提供的在线语音包。
如苹果的「Ting-Ting」和谷歌的「Google 普通话(中国大陆)」。

在构建 SpeechSynthesisUtterance 的时候,传入支持的语音包的 lang 和 voice (对应 SpeechSynthesisVoice 的 name)就可以选择语音包了。

通过这个接口,我们可以给使用 Google Chrome 的浏览器用户提供来自谷歌的语音合成功能。

探索 SpeechSynthesis 的原理下面,
我们通过 Chromium 的源代码探索 Google Chrome 的 SpeechSynthesis 是怎么实现的。

Chromium 里面的 SpeechSynthesis 类都是在 speech 模块 中实现的。
其中,该模块包含 语音识别(speechRecognizer)和 语音合成 (SpeechSynthesis)两大功能的实现。

看语音合成的头文件就大概知道有哪些功能:

speech_synthesis_impl.h: SpeechSynthesis 实现的后端,分派任务给 TTS Conreoller
tts_controller_impl.h: 语音合成的控制器,集成了获取语音包、设置引擎、播放、暂停等功能
tts_platform_impl.h: 管理平台相关的语音包,具体的实现在
语音包怎么来的?



我们可以先看 tts_controller_impl 里面的 GetVoices 实现:

void TtsControllerImpl::GetVoices
(BrowserContext* browser_context,std::vector<VoiceData>* out_voices) {
  TtsPlatform* tts_platform = GetTtsPlatform(); // 获取操作系统相关的 TTS
  if (tts_platform) { // 当前平台存在 TTS
    // Ensure we have all built-in voices loaded. This is a no-op if already
    // loaded.
    tts_platform->LoadBuiltInTtsEngine(browser_context); // 加载本地的 TTS
    if (tts_platform->PlatformImplAvailable())
      tts_platform->GetVoices(out_voices);
  }

  if (browser_context) {
    TtsControllerDelegate* delegate = GetTtsControllerDelegate();
    if (delegate && delegate->GetTtsEngineDelegate()) // 浏览器自带的 TTS
      delegate->GetTtsEngineDelegate()->GetVoices(browser_context, out_voices);
  }
}


简单来说,SpeechSynthesis 的语音包来自两大地方——操作系统自带和浏览器自带。所以我们才发现 window.speechSynthesis.getVoices() 拿到的语音包存在一部分是 Apple 提供的,一部分是 Google 提供的。

这个技术是最常用在谷歌浏览器,H5的语音合成:Speech Synthesis ,基本上兼容大部分的主流浏览器,但是完全不兼容IE,需要设置IE需要写一些兼容的语句。Microsoft Edge浏览器的支持较好,支持的语言包比较丰富。

2、项目界面截图

3、封装class类方法(实例化调用)

// 语音播报的函数
export default class SpeakVoice {
  constructor(vm, config) {
    let that = this
    that._vm = vm
    that.config = {
      text: '春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明!',
      volume: 1, // 声音音量:1,范围从0到1
      rate: 1,   // 设置语速:1,范围从0到100
      labelData:{
        name:''
      },
      ...config
    }
    that.synth = window.speechSynthesis // 启用文本
    that.instance = new SpeechSynthesisUtterance()
    that.instance.lang = "zh-CN"; // 使用的语言:中文
    that.status = '初始化'
    that.isload = false;
    that.initVoice();
  }
  // 初始化
  initVoice(){
    let that = this
    if(that.isload){
      return false
    }else{
      that.isload = true
      that.instance.text = that.config.text; // 文字内容: 测试内容
      that.instance.volume = that.config.volume;
      that.instance.rate = that.config.rate;

      that.instance.onstart = e => {
        that.status = '开始播放'
      }
      that.instance.onend = e => {
        that.status = '结束播放'
      }
      that.instance.onpause = e => {
        this.status = "暂停播放"
      }

    }
    /*let speech = that.getSpeechVoices();
    speech.then((voices) => {
      voices = voices.filter(item => (item.lang.indexOf('zh-') > -1 && item.localService));
      if (voices.length === 0) {
        console.error('没有可用的中文语音!');  //中文包可以调试多种语言包切换
      }else {
        //实例化播报内容
        that.instance.lang = "zh-CN"; // 使用的语言:中文
        that.instance.text = '测试内容'; // 文字内容: 测试内容
        that.instance.volume = 1
        that.instance.rate = 1
        that.instance.voice = voices[0]
        that.synth.speak(that.instance); // 播放
      }
    });*/
  }
  // 语音开始
  handleSpeak() {
    this.synth.speak(this.instance); // 播放
  }
  // 语音队列重播
  handleReply() {
    this.handleCancel();
    this.handleSpeak()
  }
  // 语音队列删除 , 删除队列中所有的语音.如果正在播放,则直接停止
  handleCancel() {
    this.synth.cancel(this.instance);
  }
  // 语音暂停, 暂停语音该次语音播放
  handleStop() {
    this.synth.pause(this.instance);
  }
  // 恢复暂停的语音
  handleResume(){
    this.synth.resume(this.instance) //恢复暂停的语音
  }



  //获取语言包数据, 这个接口需要一点时间,改成异步的
  getSpeechVoices() {
    let that = this
    return new Promise(function (resolve, reject) {
        let id;
        id = setInterval(() => {
          if (that.synth.getVoices().length !== 0) {
            resolve(that.synth.getVoices());
            clearInterval(id);
          }
        }, 10);
      }
    )
  }

  destory () {
    this.handleCancel()
  }
}


4、实例化父组件

<i title="播放" class="el-icon-microphone" @click="actMicroFun"></i>


import SpeakVoice from './speak-voice.js'


actMicroFun () {
  let speakVoice = new SpeakVoice();
  speakVoice.handleReply();
}

5、关于语音api解析

属性设置
SpeechSynthesisUtterance.lang 获取并设置话语的语言
SpeechSynthesisUtterance.pitch 获取并设置话语的音调(值越大越尖锐,越低越低沉)
SpeechSynthesisUtterance.rate 获取并设置说话的速度(值越大语速越快,越小语速越慢)
SpeechSynthesisUtterance.text 获取并设置说话时的文本
SpeechSynthesisUtterance.voice 获取并设置说话的声音
SpeechSynthesisUtterance.volume 获取并设置说话的音量


函数设置
speechSynthesis.speak() 将对应的实例添加到语音队列中
speechSynthesis.cancel() 删除队列中所有的语音.如果正在播放,则直接停止
speechSynthesis.pause() 暂停语音
speechSynthesis.resume() 恢复暂停的语音
speechSynthesis.getVoices() 获取支持的语言数组.

<template> <div class="container"> <el-input v-model="text" type="textarea" :rows="5" placeholder="输入要换的文本" /> <el-select v-model="selectedVoice" placeholder="选择发音人" class="mt-20" > <el-option v-for="voice in voices" :key="voice.name" :label="voice.name" :value="voice" /> </el-select> <div class="sliders mt-20"> <div class="slider-item"> <span>语速 ({{ rate }}x)</span> <el-slider v-model="rate" :min="0.5" :max="2" :step="0.1"/> </div> <div class="slider-item"> <span>音调 ({{ pitch }})</span> <el-slider v-model="pitch" :min="0" :max="2" :step="0.1"/> </div> <div class="slider-item"> <span>音量 ({{ volume }})</span> <el-slider v-model="volume" :min="0" :max="1" :step="0.1"/> </div> </div> <el-button type="primary" class="mt-20" @click="generateSpeech" > 生成语音 </el-button> </div> </template> <script setup> import { ref, reactive, onMounted } from 'vue' const text = ref('') const selectedVoice = ref(null) const rate = ref(1) const pitch = ref(1) const volume = ref(1) const voices = ref([]) onMounted(() => { // 加载可用语音列表 speechSynthesis.addEventListener('voiceschanged', () => { voices.value = speechSynthesis.getVoices() }) }) const generateSpeech = () => { if (!text.value) return const utterance = new SpeechSynthesisUtterance(text.value) utterance.voice = selectedVoice.value utterance.rate = rate.value utterance.pitch = pitch.value utterance.volume = volume.value speechSynthesis.speak(utterance) } </script> <style scoped> .container { max-width: 800px; margin: 20px auto; padding: 20px; } .mt-20 { margin-top: 20px; } .slider-item { margin: 15px 0; } </style>生成语言按钮放在文本输入框的右下角,并且是一个三角播放按钮
03-09
<think>好的,我要帮助用户解决在Vue项目中调整生成语音按钮的位置样式的问题。用户希望将按钮放在文本输入框的右下角,并改为三角形播放按钮。首先,我得回忆一下Vue中处理表单元素样式的方法。 首先,结构方面,可能要将输入框按钮裹在一个容器中,使用相对定位绝对定位来定位按钮的位置。比如,父容器设置position: relative,按钮使用position: absolute,然后设置rightbottom属性来定位到右下角。同时,要注意调整padding,避免文本被按钮遮挡。 然后是按钮样式的修改。用户想要三角形,可以用CSS的border属性来画三角形。比如,设置宽度高度为0,然后通过边框的透明效果来形成一个三角形。可能要调整border的宽度颜色,以及使用transform来旋方向,比如旋45度形成播放按钮的样式。 另外,用户可能还要处理按钮的交互,比如点击事件。在Vue中,可以用@click绑定方法,触发语音生成的功能。同时,可能要动态改变按钮的样式,比如播放暂停状态的同三角形方向,这可以通过条件名或者动态样式来实现。 还要考虑响应式设计,确保按钮在同屏幕尺寸下位置正确。可能要使用flex布局或者调整定位的值。此外,注意按钮的层级,避免被其他元素覆盖。 检查是否有相关的引用内容。比如引用[2]提到用CSS实现三角形,这正好适用。用户要至少两种方法,但这里可能只要一种,过可以提及另一种方法作为备选,比如使用伪元素或SVG图标。 最后,代码示例要结构清晰,括HTML模板部分、CSS样式部分Vue的方法部分。确保使用Vue的语法正确,比如使用@click而是onclick,样式使用scoped避免全局污染。 总结步骤:创建父容器,定位按钮,设计三角形样式,绑定事件,调整细节如内边距悬停效果。验证代码的正确性,确保按钮位置正确,样式美观,功能正常。</think>### 如何在Vue项目中将语音按钮置于输入框右下角并改为三角按钮? #### 1. 结构布局 在Vue模板中,使用**相对定位容器裹输入框按钮**: ```html <template> <div class="input-wrapper"> <textarea v-model="text" class="text-input" placeholder="请输入文本" ></textarea> <button @click="generateSpeech" class="voice-button" ></button> </div> </template> ``` #### 2. CSS定位与三角样式实现 通过**绝对定位+边框技巧**实现右下角三角按钮: ```css /* 引用自CSS三角形实现原理[^2] */ .input-wrapper { position: relative; width: 300px; } .text-input { width: 100%; padding: 12px 40px 12px 12px; /* 右侧留出按钮空间 */ min-height: 100px; } .voice-button { position: absolute; right: 10px; bottom: 10px; width: 0; height: 0; border: 12px solid transparent; border-left-color: #409eff; /* 蓝色三角 */ background: none; cursor: pointer; transform: rotate(0deg); /* 默认播放箭头朝右 */ transition: all 0.3s; } /* 悬停及状态变化 */ .voice-button:hover { border-left-color: #66b1ff; } .voice-button.playing { transform: rotate(90deg); /* 播放状态变为暂停样式 */ } ``` #### 3. Vue交互逻辑 添加状态管理与方法绑定: ```javascript <script> export default { data() { return { text: '', isPlaying: false } }, methods: { generateSpeech() { this.isPlaying = !this.isPlaying // 调用语音生成API逻辑... } } } </script> ``` #### 关键实现要点: 1. **定位方案**:父容器相对定位提供定位基准,按钮绝对定位通过`right:10px; bottom:10px`固定到右下角 2. **三角形实现**:利用CSS边框特性,设置三边透明、一边显色形成三角形 3. **交互优化**:通过padding预留按钮空间防止文字遮挡,添加悬停动画增强体验
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值