feat: 语音气泡浅蓝 + 页眉图标展示全部视觉状态 + 简化自动问候
- /voice 页右下角气泡文本改浅蓝色(最新 #4cc2ff / 历史 #6fb5ea) - GlobalVision 广播 dpm:vision-status(status/cameraOn/gesture/faces) - 页眉图标四态:默认灰(探索) → 识别中浅蓝(摄像头开/检测/面向) → 手势绿(惊喜) → 异常红 - 自动调用语音助手问候语简化为「您好呀,需要我的帮助吗?」
This commit is contained in:
@@ -31,6 +31,18 @@ export default function GlobalVision() {
|
||||
window.dispatchEvent(new CustomEvent('dpm:hand-track', { detail: { hands: vision.hands } }));
|
||||
}, [vision.hands]);
|
||||
|
||||
// 视觉识别状态广播:dpm:vision-status(页眉图标展示全部视觉相关状态)
|
||||
useEffect(() => {
|
||||
window.dispatchEvent(new CustomEvent('dpm:vision-status', {
|
||||
detail: {
|
||||
status: vision.status, // off|loading|detecting|facing|triggered|silent|error
|
||||
cameraOn: vision.cameraOn,
|
||||
gesture: vision.gesture, // raise 等最近一次手势
|
||||
faces: vision.faces,
|
||||
},
|
||||
}));
|
||||
}, [vision.status, vision.cameraOn, vision.gesture, vision.faces]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 摄像头驱动(隐藏,仅供全局抽帧识别) */}
|
||||
|
||||
@@ -24,6 +24,7 @@ export default function PageHeader() {
|
||||
const [now, setNow] = useState(new Date());
|
||||
const [aboutOpen, setAboutOpen] = useState(false);
|
||||
const [gestureHit, setGestureHit] = useState(false); // 识别到手势 → 惊喜图标 3s
|
||||
const [vision, setVision] = useState({ status: 'off', cameraOn: false, gesture: null }); // 视觉识别状态
|
||||
const pageIdx = usePageIndex();
|
||||
const gestureTimer = useRef(null);
|
||||
|
||||
@@ -32,7 +33,7 @@ export default function PageHeader() {
|
||||
return () => clearInterval(iv);
|
||||
}, []);
|
||||
|
||||
// 全局手势事件 → 右上角状态图标(默认灰色探索 → 识别到手势变绿色惊喜)
|
||||
// 全局手势事件 → 惊喜图标 3s
|
||||
useEffect(() => {
|
||||
const onGesture = (e) => {
|
||||
if (!e.detail || !e.detail.type) return;
|
||||
@@ -47,6 +48,17 @@ export default function PageHeader() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 视觉识别状态(GlobalVision 广播):off/loading/detecting/facing/triggered/silent/error
|
||||
useEffect(() => {
|
||||
const onStatus = (e) => setVision(e.detail || {});
|
||||
window.addEventListener('dpm:vision-status', onStatus);
|
||||
return () => window.removeEventListener('dpm:vision-status', onStatus);
|
||||
}, []);
|
||||
|
||||
// 图标状态:手势(绿) > 视觉识别中(浅蓝) > 默认(灰)
|
||||
const visionActive = vision.cameraOn && vision.status !== 'error' && vision.status !== 'off';
|
||||
const visionError = vision.status === 'error';
|
||||
|
||||
const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
||||
const week = ['日', '一', '二', '三', '四', '五', '六'][now.getDay()];
|
||||
const timeStr = now.toLocaleTimeString('zh-CN', { hour12: false });
|
||||
@@ -101,9 +113,9 @@ export default function PageHeader() {
|
||||
{status}
|
||||
</div>
|
||||
<button
|
||||
className={`bd-about-btn${gestureHit ? ' gesture-hit' : ''}`}
|
||||
className={`bd-about-btn${gestureHit ? ' gesture-hit' : visionActive ? ' vision-active' : visionError ? ' vision-error' : ''}`}
|
||||
type="button"
|
||||
title={gestureHit ? '识别到手势' : '关于 PineSound'}
|
||||
title={gestureHit ? '识别到手势' : visionActive ? `视觉识别中(${vision.status})` : visionError ? '视觉识别异常' : '关于 PineSound'}
|
||||
aria-label="关于 PineSound"
|
||||
onClick={() => setAboutOpen(true)}
|
||||
>
|
||||
|
||||
@@ -133,7 +133,7 @@ export default function VoiceAssistant() {
|
||||
const live = LIVE_STATES.has(state);
|
||||
|
||||
// ── 自动问候生命周期状态(须在 start 之前定义:start 依赖数组引用 sendGreeting)──
|
||||
const GREETING_TEXT = '你好!欢迎来到园区,我是智能语音助手,请问有什么可以帮你的吗?';
|
||||
const GREETING_TEXT = '您好呀,需要我的帮助吗?';
|
||||
const GREETING_WAIT_MS = 25000; // 问候播完后等待用户回答的超时
|
||||
const voiceStateRef = useRef(state);
|
||||
voiceStateRef.current = state;
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
|
||||
/* ============ 语音气泡:持续展示 · 最新大字 · 历史小字 ============ */
|
||||
.bubble {
|
||||
color: #1d2c44; /* 浅色背景适配(原版深色文字在浅色下不可见) */
|
||||
color: #4cc2ff; /* 浅蓝色文本(视觉识别对话风格) */
|
||||
}
|
||||
.bubble .bubble-role {
|
||||
font-size: 10px;
|
||||
@@ -172,13 +172,13 @@
|
||||
font-size: 21px;
|
||||
font-weight: 600;
|
||||
line-height: 1.45;
|
||||
color: #1d2c44;
|
||||
color: #4cc2ff;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
/* 历史消息:小字,向上排列 */
|
||||
.bubble.history .bubble-body {
|
||||
font-size: 12.5px;
|
||||
line-height: 1.5;
|
||||
color: #4a5b76;
|
||||
color: #6fb5ea;
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
@@ -2599,6 +2599,24 @@
|
||||
0%, 100% { box-shadow: 0 0 8px rgba(23, 178, 106, 0.3); transform: scale(1); }
|
||||
50% { box-shadow: 0 0 18px rgba(23, 178, 106, 0.6); transform: scale(1.08); }
|
||||
}
|
||||
/* 视觉识别中:浅蓝色(摄像头开启 / 检测中 / 面向 / 触发) */
|
||||
.bd-about-btn.vision-active {
|
||||
color: var(--bd-cyan, #4cc2ff);
|
||||
border-color: rgba(76, 194, 255, 0.5);
|
||||
background: rgba(76, 194, 255, 0.1);
|
||||
box-shadow: 0 0 10px rgba(76, 194, 255, 0.35);
|
||||
animation: bdVisionPulse 2s ease-in-out infinite;
|
||||
}
|
||||
@keyframes bdVisionPulse {
|
||||
0%, 100% { box-shadow: 0 0 6px rgba(76, 194, 255, 0.25); }
|
||||
50% { box-shadow: 0 0 14px rgba(76, 194, 255, 0.5); }
|
||||
}
|
||||
/* 视觉识别异常:红色 */
|
||||
.bd-about-btn.vision-error {
|
||||
color: var(--bd-red, #f04438);
|
||||
border-color: rgba(240, 68, 56, 0.5);
|
||||
background: rgba(240, 68, 56, 0.08);
|
||||
}
|
||||
|
||||
.about-overlay {
|
||||
position: fixed;
|
||||
|
||||
Reference in New Issue
Block a user