Files
ks-app-employment-service/hook/useSpeechReader.js
2025-04-07 09:10:55 +08:00

109 lines
2.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
}
}