feat: 右上角图标改造为手势状态指示器

- 默认灰色「探索」罗盘图标(Compass)
- 识别到手势(dpm:gesture)→ 变绿色「惊喜」表情(EmojiSurprised)+ 光晕脉冲,3s 后恢复
- title 同步切换(关于 PineSound / 识别到手势),点击仍打开关于弹窗
- 新增 explore/surprise 图标映射与手势状态样式
This commit is contained in:
Pine
2026-08-18 06:21:55 +08:00
parent 6b7fd0ec95
commit 75498027f3
3 changed files with 37 additions and 6 deletions
+4 -2
View File
@@ -12,7 +12,7 @@ import {
Volume, VolumeOff, ChevronLeft, ChevronRight, PlayerPlay, PlayerPause,
Send, Trash, Eraser, Headset, CircleDot, AlertCircle,
Microphone, MicrophoneFilled, MicrophoneOff, MessageCircle,
Camera, CameraOff, HandStop, Copyright,
Camera, CameraOff, HandStop, Copyright, Compass, EmojiSurprised,
} from '@appica/icons-react';
/* 名称 → 图标组件映射(推荐的调用方式:icon="bolt" */
@@ -90,6 +90,8 @@ const NAME_MAP = {
'camera-off': CameraOff,
'hand-stop': HandStop,
copyright: Copyright,
explore: Compass,
surprise: EmojiSurprised,
};
/**
@@ -124,5 +126,5 @@ export {
Volume, VolumeOff, ChevronLeft, ChevronRight, PlayerPlay, PlayerPause,
Send, Trash, Eraser, Headset, CircleDot, AlertCircle,
Microphone, MicrophoneFilled, MicrophoneOff, MessageCircle,
Camera, CameraOff, HandStop, Copyright,
Camera, CameraOff, HandStop, Copyright, Compass, EmojiSurprised,
};
+21 -4
View File
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect, useState, useRef } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import Icon from './Icons';
import MediaHeaderControls from './MediaHeaderControls';
@@ -23,13 +23,30 @@ export default function PageHeader() {
const location = useLocation();
const [now, setNow] = useState(new Date());
const [aboutOpen, setAboutOpen] = useState(false);
const [gestureHit, setGestureHit] = useState(false); // 识别到手势 → 惊喜图标 3s
const pageIdx = usePageIndex();
const gestureTimer = useRef(null);
useEffect(() => {
const iv = setInterval(() => setNow(new Date()), 1000);
return () => clearInterval(iv);
}, []);
// 全局手势事件 → 右上角状态图标(默认灰色探索 → 识别到手势变绿色惊喜)
useEffect(() => {
const onGesture = (e) => {
if (!e.detail || !e.detail.type) return;
setGestureHit(true);
if (gestureTimer.current) clearTimeout(gestureTimer.current);
gestureTimer.current = setTimeout(() => setGestureHit(false), 3000);
};
window.addEventListener('dpm:gesture', onGesture);
return () => {
window.removeEventListener('dpm:gesture', onGesture);
if (gestureTimer.current) clearTimeout(gestureTimer.current);
};
}, []);
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 });
@@ -84,13 +101,13 @@ export default function PageHeader() {
{status}
</div>
<button
className="bd-about-btn"
className={`bd-about-btn${gestureHit ? ' gesture-hit' : ''}`}
type="button"
title="关于 PineSound"
title={gestureHit ? '识别到手势' : '关于 PineSound'}
aria-label="关于 PineSound"
onClick={() => setAboutOpen(true)}
>
<Icon name="info" size={14} />
<Icon name={gestureHit ? 'surprise' : 'explore'} size={15} />
</button>
<div className="bd-clock">
<div className="bd-clock-time">{timeStr}</div>
+12
View File
@@ -2587,6 +2587,18 @@
border-color: #2f6bff;
background: #fff;
}
/* 手势状态:识别到手势 → 绿色惊喜图标 + 光晕脉冲 */
.bd-about-btn.gesture-hit {
color: var(--bd-green, #17b26a);
border-color: rgba(23, 178, 106, 0.55);
background: rgba(23, 178, 106, 0.1);
box-shadow: 0 0 14px rgba(23, 178, 106, 0.45);
animation: bdGesturePulse 1s ease-in-out infinite;
}
@keyframes bdGesturePulse {
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); }
}
.about-overlay {
position: fixed;