Files
ks-app-employment-service/hook/useSpeechReader.js

109 lines
2.9 KiB
JavaScript
Raw Normal View History

2025-04-07 09:10:55 +08:00
import {
ref,
onBeforeUnmount,
onMounted
} from 'vue'
import {
onHide,
onUnload
} from '@dcloudio/uni-app'
function formatTextForSpeech(rawText) {
return rawText
// 去除链接 markdown 格式 [xxx](url)
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
// 去除 Markdown 语法符号
.replace(/[*_`>#\-]/g, '')
// 将换行转换为句号
.replace(/\n+/g, '。')
// 多个标点统一转为句号(表示停顿)
.replace(/[。,,.、!?;:~~…··\-\/\\]{1,}/g, '。')
// 合并多个句号
.replace(/([。]{2,})/g, '。')
// 去除多余空格
.replace(/\s+/g, ' ')
// 去除开头结尾的句号
.replace(/^[。]+|[。]+$/g, '')
.trim()
}
export function useSpeechReader() {
const isSpeaking = ref(false)
const isPaused = ref(false)
let utterance = null
const cleanMarkdown = (text) => {
return formatTextForSpeech(text)
}
const speak = (text, options = {
lang: 'zh-CN',
rate: 0.9,
pitch: 1.2
}) => {
cancel() // 重置之前的
const voices = speechSynthesis.getVoices()
const chineseVoices = voices.filter(v => v.lang.includes('zh'))
// console.log(chineseVoices.map((item) => item.name))
const cleanText = cleanMarkdown(text)
utterance = new SpeechSynthesisUtterance(cleanText)
// utterance.voice = chineseVoices.find(v => v.name === 'Shelley')
utterance.lang = options.lang || 'zh-CN'
utterance.rate = options.rate || 1
utterance.pitch = options.pitch || 1.1 // 音调0 - 2偏高比较柔和
utterance.onend = () => {
isSpeaking.value = false
isPaused.value = false
}
speechSynthesis.speak(utterance)
isSpeaking.value = true
isPaused.value = false
}
const pause = () => {
if (isSpeaking.value && !isPaused.value) {
speechSynthesis.pause()
isPaused.value = true
}
}
const resume = () => {
if (isSpeaking.value && isPaused.value) {
speechSynthesis.resume()
isPaused.value = false
}
}
const cancel = () => {
speechSynthesis.cancel()
isSpeaking.value = false
isPaused.value = false
}
// 页面刷新/关闭时
onMounted(() => {
if (typeof window !== 'undefined') {
window.addEventListener('beforeunload', cancel)
}
})
onBeforeUnmount(() => {
cancel()
if (typeof window !== 'undefined') {
window.removeEventListener('beforeunload', cancel)
}
})
onHide(cancel)
onUnload(cancel)
return {
speak,
pause,
resume,
cancel,
isSpeaking,
isPaused,
}
}