修复:兼容Chrome公屏语音播放限制
问题:Chrome在页面没有用户操作时拒绝speechSynthesis并返回not-allowed,导致同机Safari有声但Chrome无声。 实现:两个公屏增加一次性声音启用操作,在用户手势中解锁语音;保留每次三遍播报,并补充暂停恢复、失败提示和回归测试。 复现:直接打开公屏页面后由员工端叫号,Chrome不播放声音。
This commit is contained in:
63
web/src/hooks/useSpeechAnnouncements.ts
Normal file
63
web/src/hooks/useSpeechAnnouncements.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export type SpeechAnnouncementStatus = "locked" | "enabling" | "ready" | "error" | "unsupported";
|
||||
|
||||
const ANNOUNCEMENT_REPEAT_COUNT = 3;
|
||||
|
||||
function speechSupported(): boolean {
|
||||
return typeof window !== "undefined"
|
||||
&& "speechSynthesis" in window
|
||||
&& typeof SpeechSynthesisUtterance !== "undefined";
|
||||
}
|
||||
|
||||
function createUtterance(text: string): SpeechSynthesisUtterance {
|
||||
const utterance = new SpeechSynthesisUtterance(text);
|
||||
utterance.lang = "zh-CN";
|
||||
utterance.rate = 0.9;
|
||||
return utterance;
|
||||
}
|
||||
|
||||
export function useSpeechAnnouncements() {
|
||||
const [status, setStatus] = useState<SpeechAnnouncementStatus>(() => (
|
||||
speechSupported() ? "locked" : "unsupported"
|
||||
));
|
||||
|
||||
const enable = useCallback(() => {
|
||||
if (!speechSupported()) {
|
||||
setStatus("unsupported");
|
||||
return;
|
||||
}
|
||||
|
||||
const synthesis = window.speechSynthesis;
|
||||
const utterance = createUtterance("叫号声音已开启。");
|
||||
utterance.onstart = () => setStatus("ready");
|
||||
utterance.onend = () => setStatus("ready");
|
||||
utterance.onerror = (event) => {
|
||||
if (event.error !== "canceled" && event.error !== "interrupted") setStatus("error");
|
||||
};
|
||||
|
||||
setStatus("enabling");
|
||||
synthesis.cancel();
|
||||
if (synthesis.paused) synthesis.resume();
|
||||
synthesis.speak(utterance);
|
||||
}, []);
|
||||
|
||||
const announce = useCallback((announcements: string[]) => {
|
||||
if (status !== "ready" || !announcements.length || !speechSupported()) return;
|
||||
|
||||
const synthesis = window.speechSynthesis;
|
||||
synthesis.cancel();
|
||||
if (synthesis.paused) synthesis.resume();
|
||||
for (const announcement of announcements) {
|
||||
for (let repeat = 0; repeat < ANNOUNCEMENT_REPEAT_COUNT; repeat += 1) {
|
||||
const utterance = createUtterance(announcement);
|
||||
utterance.onerror = (event) => {
|
||||
if (event.error !== "canceled" && event.error !== "interrupted") setStatus("error");
|
||||
};
|
||||
synthesis.speak(utterance);
|
||||
}
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
return { announce, enable, status };
|
||||
}
|
||||
Reference in New Issue
Block a user