一、需求分析
① 根据显示的数据列表依次播报每一则数据的主题;
② 静音功能:点击当前正在播报的语音时,暂停继续播报本则信息,切换下一则信息进行播报;点击非当前正在播报的信息时,跳过开启静音的信息。
二、使用技术
① SpeechSynthesis:语音服务的控制接口;它可以用于获取设备上关于可用的合成声音的信息,开始、暂停语音,或除此之外的其他命令。
本次使用的api有:pause()、resume()、speak()
② SpeechSynthesisUtterance
MDN地址:SpeechSynthesisUtterance
三、技术栈
node.js、vue3、typescript、ES6、axios
三、服务器搭建
本次使用nodejs 简易搭建服务器
四、功能实现
①封装SpeechSynthesis及SpeechSynthesisUtterance的API
* 播放声音
*/
const synth = window.speechSynthesis
const msg = new SpeechSynthesisUtterance()
export class WarningsSpeech {
texts: Array<any>
lang: string
speed: number
pauseFlag: boolean
flag: number
constructor(texts = [], lang = 'zh-CN', speed = 1) {
this.texts = texts
this.lang = lang
this.speed = speed
this.pauseFlag = false
this.flag = 0
}
init() {
msg.lang = this.lang
synth.cancel()
}
speech() {
this.init()
if (this.pauseFlag) {
this._judgePlay()
this.resume()
} else {
msg.text = this.texts[this.flag]?.text ?? ''
synth.speak(msg)
}
msg.onend = (e) => {
this._judgePlay()
}
}
_judgePlay() {
for (let i = this.flag + 1; i < this.texts.length; i++) {
if (this.texts[i].isMuted === false) {
this.flag = i
msg.text = this.texts[this.flag].text
synth.speak(msg)
return
}
}
}
pause() {
synth.pause()
this.pauseFlag = true
}
resume() {
synth.resume()
this.pauseFlag = false
}
muted() {
if (!this.pauseFlag) {
this.pause()
}
Promise.resolve(this.pauseFlag)
.then((res) => {
if (res) {
this.speech()
}
})
.then(() => {
this.pauseFlag = false
})
}
cancel() {
synth.pause()
this.pauseFlag = true
}
}
复制代码
② 组件调用
<div class="speech">
<h1>文字转语音功能</h1>
<ul class="speech_container">
<template v-for="(items, index) in dataItems" :key="items.id">
<li class="speech_slide">
<span>{{ items.title }}</span>
<i @click="handleMuted(items, index)" :class="{ disabled_i: items.isMuted }">静音</i>
</li>
</template>
</ul>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { getData } from '@/api/speech'
import { WarningsSpeech } from '@/utils/speechSythesis'
const dataItems = ref([])
const speech = ref<any>(null)
onMounted(() => {
handleGetData()
})
/**
* 获取数据
*/
const handleGetData = async () => {
try {
const { data: res } = await getData()
const { data } = res
dataItems.value = data.dataList.map((o: any) => {
return {
...o,
text: o.title,
isMuted: false,
}
})
speech.value = new WarningsSpeech(dataItems.value)
speech.value.speech()
} catch (e) {
console.error('请求错误,请联系管理员!')
}
}
/**
* 静音按钮功能
*/
const handleMuted = (item: any, index: number) => {
if (index === speech.value.flag) {
speech.value.muted()
}
item.isMuted = !item.isMuted
dataItems.value[index].isMuted = item.isMuted
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0;
}
a {
color: #42b983;
}
.speech_container {
width: 15vw;
margin: 0 auto;
padding: 20px 20px 50px;
color: rgba(255, 255, 255, 0.85);
border-radius: 8px 8px 0 0;
background-image: -webkit-linear-gradient(#24c5b6, #b2ebd6);
}
.speech_slide {
width: 100%;
display: inline-flex;
justify-content: space-between;
align-items: center;
margin: 5px 0;
}
.speech_slide i {
font-style: normal;
display: inline-block;
padding: 2px 10px;
box-sizing: border-box;
border-radius: 6px;
color: #24c5b6;
font-size: 12px;
background: rgba(255, 255, 255, 0.85);
cursor: pointer;
}
.speech_slide .disabled_i {
background: rgba(255, 255, 255, 0.55);
color: #fff;
cursor: not-allowed;
}
</style>
复制代码
五、代码分析
① node:使用node搭建服务器后,在vue.config.js中配置代理,才能够成功发送请求,否则会出现404;
② SpeechSynthesis及SpeechSynthesisUtterance功能封装:关键在于点击静音按钮判断是否为正在播报的语音,如果是,中断播报当前语音,并播报下一则未被静音的信息,实现方法为WarningsSpeech类中的muted方法;_judgePlay为类的私有属性,用于在语音结束的回调中,找到下一个未被静音的信息进行语音播报
六、成果展示
七、SpeechSynthesis API兼容性:
由此可知,大多数主流浏览器已支持此API。
七、结束
源码已上传至github(github.com/duangkey/sp…)
世界上最遥远的距离不是你我通过一根网线相连,而是你看到了,却没留下你的star!!!