style: DeviceScreen.tsx清理全部emoji符号
- 移除注释中所有emoji标记(checkmark/new/wrench/search等) - 移除日志输出中的emoji(控制权/警告/错误等) - 移除UI文本中的emoji(操作已禁用/丢帧警告/全屏按钮) - 清理Vite缓存解决pendingSizeRef旧代码残留问题
This commit is contained in:
@@ -27,12 +27,12 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
const [imageSize, setImageSize] = useState<{ width: number, height: number } | null>(null)
|
||||
const [isFullscreen, setIsFullscreen] = useState(false)
|
||||
|
||||
// ✅ FPS 计算:使用滑动窗口统计真实帧率
|
||||
// FPS 计算:使用滑动窗口统计真实帧率
|
||||
const fpsFrameTimesRef = useRef<number[]>([])
|
||||
const [displayFps, setDisplayFps] = useState(0)
|
||||
const lastFpsUpdateRef = useRef(0)
|
||||
|
||||
// ✅ 帧渲染控制:只保留最新帧,用 rAF 驱动渲染
|
||||
// 帧渲染控制:只保留最新帧,用 rAF 驱动渲染
|
||||
const latestFrameRef = useRef<any>(null)
|
||||
const rafIdRef = useRef<number>(0)
|
||||
const isRenderingRef = useRef(false)
|
||||
@@ -42,7 +42,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
// 避免不同采集模式(MediaProjection/无障碍截图)帧尺寸不一致导致闪烁
|
||||
const lockedCanvasSizeRef = useRef<{ width: number, height: number } | null>(null)
|
||||
|
||||
// ✅ 添加控制权状态跟踪,避免重复申请
|
||||
// 添加控制权状态跟踪,避免重复申请
|
||||
const [isControlRequested, setIsControlRequested] = useState(false)
|
||||
const [currentWebSocket, setCurrentWebSocket] = useState<any>(null)
|
||||
const [lastControlRequestTime, setLastControlRequestTime] = useState<number>(0)
|
||||
@@ -53,7 +53,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
|
||||
const device = connectedDevices.find(d => d.id === deviceId)
|
||||
|
||||
// 📊 画质控制状态
|
||||
// 画质控制状态
|
||||
const [currentProfile, setCurrentProfile] = useState('medium')
|
||||
const [showQualityPanel, setShowQualityPanel] = useState(false)
|
||||
const [networkStats, setNetworkStats] = useState({ fps: 0, dropRate: 0, avgFrameSize: 0 })
|
||||
@@ -62,14 +62,14 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
const feedbackTimerRef = useRef<number>(0)
|
||||
const lastFeedbackTimeRef = useRef(0)
|
||||
|
||||
// ✅ 安全地通知父组件屏幕尺寸变化(在 useEffect 中而非渲染期间)
|
||||
// 安全地通知父组件屏幕尺寸变化(在 useEffect 中而非渲染期间)
|
||||
useEffect(() => {
|
||||
if (imageSize && onScreenSizeChange) {
|
||||
onScreenSizeChange(imageSize)
|
||||
}
|
||||
}, [imageSize, onScreenSizeChange])
|
||||
|
||||
// 📊 画质反馈:定期向服务端报告网络质量
|
||||
// 画质反馈:定期向服务端报告网络质量
|
||||
useEffect(() => {
|
||||
if (!webSocket || !deviceId) return
|
||||
|
||||
@@ -112,23 +112,23 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
}
|
||||
}, [webSocket, deviceId, displayFps])
|
||||
|
||||
// 📊 切换画质档位
|
||||
// 切换画质档位
|
||||
const handleSetProfile = useCallback((profileKey: string) => {
|
||||
if (!webSocket) return
|
||||
webSocket.emit('set_quality_profile', { deviceId, profile: profileKey })
|
||||
setCurrentProfile(profileKey)
|
||||
}, [webSocket, deviceId])
|
||||
|
||||
// ✅ 监听屏幕数据的独立useEffect,避免与控制权逻辑混合
|
||||
// 监听屏幕数据的独立useEffect,避免与控制权逻辑混合
|
||||
useEffect(() => {
|
||||
if (!webSocket) return
|
||||
|
||||
const handleScreenData = (data: any) => {
|
||||
if (data.deviceId === deviceId) {
|
||||
// 📊 帧计数用于质量反馈
|
||||
// 帧计数用于质量反馈
|
||||
frameCountRef.current++
|
||||
|
||||
// ✅ 过滤黑屏帧:Base64长度<4000字符(≈3KB JPEG)几乎肯定是黑屏/空白帧
|
||||
// 过滤黑屏帧:Base64长度<4000字符(约3KB JPEG)几乎肯定是黑屏/空白帧
|
||||
// 正常480×854 JPEG即使最低质量也>8000字符
|
||||
const dataLen = typeof data.data === 'string' ? data.data.length : 0
|
||||
const MIN_VALID_FRAME_LENGTH = 4000
|
||||
@@ -142,18 +142,18 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
return
|
||||
}
|
||||
|
||||
// 🔍 诊断:记录数据到达频率
|
||||
// 诊断:记录数据到达频率
|
||||
if (frameCountRef.current % 30 === 0) {
|
||||
console.log(`[屏幕数据] 已收到 ${frameCountRef.current} 帧, 数据大小: ${dataLen}, 渲染中: ${isRenderingRef.current}, 解码中: ${decodingRef.current}`)
|
||||
}
|
||||
|
||||
// ✅ 只保存最新帧引用,不立即解码
|
||||
// 只保存最新帧引用,不立即解码
|
||||
latestFrameRef.current = data
|
||||
|
||||
// 只在首帧时更新loading状态,避免每帧触发重渲染
|
||||
if (isLoading) setIsLoading(false)
|
||||
|
||||
// ✅ 如果没有正在进行的渲染循环,启动一个
|
||||
// 如果没有正在进行的渲染循环,启动一个
|
||||
if (!isRenderingRef.current) {
|
||||
isRenderingRef.current = true
|
||||
renderLatestFrame()
|
||||
@@ -176,7 +176,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
}
|
||||
}, [webSocket, deviceId])
|
||||
|
||||
// ✅ 控制权管理的独立useEffect,只在必要时申请/释放
|
||||
// 控制权管理的独立useEffect,只在必要时申请/释放
|
||||
useEffect(() => {
|
||||
if (!webSocket || !deviceId) return
|
||||
|
||||
@@ -189,13 +189,13 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
// 只在未申请过控制权时申请,且防止重复发送
|
||||
if (!isControlRequested) {
|
||||
const now = Date.now()
|
||||
// 🔧 防止短时间内重复发送请求(2秒内只能发送一次)
|
||||
// 防止短时间内重复发送请求(2秒内只能发送一次)
|
||||
if (now - lastControlRequestTime < 2000) {
|
||||
console.log('⚠️ 控制权请求过于频繁,跳过')
|
||||
console.log('控制权请求过于频繁,跳过')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('🎮 申请设备控制权:', deviceId)
|
||||
console.log('申请设备控制权:', deviceId)
|
||||
setLastControlRequestTime(now)
|
||||
webSocket.emit('client_event', {
|
||||
type: 'REQUEST_DEVICE_CONTROL',
|
||||
@@ -208,19 +208,19 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
const handleControlResponse = (data: any) => {
|
||||
if (data.deviceId === deviceId) {
|
||||
if (data.success) {
|
||||
console.log('✅ 控制权获取成功:', deviceId)
|
||||
console.log('控制权获取成功:', deviceId)
|
||||
} else {
|
||||
console.warn('❌ 控制权获取失败:', data.message)
|
||||
console.warn('控制权获取失败:', data.message)
|
||||
// 如果失败,允许重新申请
|
||||
setIsControlRequested(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 监听控制错误,自动重新申请控制权
|
||||
// 监听控制错误,自动重新申请控制权
|
||||
const handleControlError = (data: any) => {
|
||||
if (data.deviceId === deviceId && data.error === 'NO_PERMISSION') {
|
||||
console.warn('⚠️ 检测到权限丢失,重新申请控制权:', deviceId)
|
||||
console.warn('检测到权限丢失,重新申请控制权:', deviceId)
|
||||
setIsControlRequested(false)
|
||||
}
|
||||
}
|
||||
@@ -235,16 +235,16 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
|
||||
// 只有在已申请过控制权时才释放
|
||||
if (isControlRequested) {
|
||||
console.log('🔓 释放设备控制权:', deviceId)
|
||||
console.log('释放设备控制权:', deviceId)
|
||||
webSocket.emit('client_event', {
|
||||
type: 'RELEASE_DEVICE_CONTROL',
|
||||
data: { deviceId }
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [webSocket, deviceId, isControlRequested, currentWebSocket]) // 🔧 不包含lastControlRequestTime避免重复执行
|
||||
}, [webSocket, deviceId, isControlRequested, currentWebSocket]) // 不包含lastControlRequestTime避免重复执行
|
||||
|
||||
// ✅ 高性能渲染:createImageBitmap 离屏解码 + 持续 rAF 循环
|
||||
// 高性能渲染:createImageBitmap 离屏解码 + 持续 rAF 循环
|
||||
const decodingRef = useRef(false)
|
||||
|
||||
const renderLatestFrame = useCallback(() => {
|
||||
@@ -564,7 +564,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
}
|
||||
}
|
||||
|
||||
// 🆕 显示长按拖拽开始指示器
|
||||
// 显示长按拖拽开始指示器
|
||||
const showLongPressDragStartIndicator = (x: number, y: number) => {
|
||||
const container = canvasRef.current?.parentElement
|
||||
if (!container) return
|
||||
@@ -603,7 +603,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
return indicator
|
||||
}
|
||||
|
||||
// 🆕 显示长按拖拽路径指示器
|
||||
// 显示长按拖拽路径指示器
|
||||
const showLongPressDragPath = (startX: number, startY: number, endX: number, endY: number) => {
|
||||
const container = canvasRef.current?.parentElement
|
||||
if (!container) return
|
||||
@@ -687,15 +687,15 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [dragStart, setDragStart] = useState<{x: number, y: number} | null>(null)
|
||||
|
||||
// 🆕 添加长按处理
|
||||
// 添加长按处理
|
||||
const [isLongPressTriggered, setIsLongPressTriggered] = useState(false)
|
||||
const longPressTimerRef = useRef<number | null>(null)
|
||||
|
||||
// 🆕 添加长按拖拽处理状态
|
||||
// 添加长按拖拽处理状态
|
||||
const [isLongPressDragging, setIsLongPressDragging] = useState(false)
|
||||
const [longPressDragStartPos, setLongPressDragStartPos] = useState<{x: number, y: number} | null>(null)
|
||||
|
||||
// 🆕 添加拖拽路径收集状态
|
||||
// 添加拖拽路径收集状态
|
||||
const [dragPath, setDragPath] = useState<Array<{x: number, y: number}>>([])
|
||||
const lastMoveTimeRef = useRef<number>(0)
|
||||
|
||||
@@ -738,7 +738,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
|
||||
}, [webSocket, device, deviceId, screenDisplay.showTouchIndicator, convertCanvasToDeviceCoords, operationEnabled])
|
||||
|
||||
// 🆕 处理长按操作
|
||||
// 处理长按操作
|
||||
const performLongPress = useCallback((canvasX: number, canvasY: number) => {
|
||||
if (!webSocket || !device) return
|
||||
|
||||
@@ -787,14 +787,14 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
|
||||
setDragStart({ x: startX, y: startY })
|
||||
|
||||
// 🆕 启动长按计时器
|
||||
// 启动长按计时器
|
||||
setIsLongPressTriggered(false)
|
||||
setIsLongPressDragging(false)
|
||||
setLongPressDragStartPos(null)
|
||||
setDragPath([]) // 🔧 清理拖拽路径
|
||||
setDragPath([]) // 清理拖拽路径
|
||||
longPressTimerRef.current = window.setTimeout(() => {
|
||||
setIsLongPressTriggered(true)
|
||||
// 🆕 长按触发后不立即执行操作,而是准备进入拖拽模式
|
||||
// 长按触发后不立即执行操作,而是准备进入拖拽模式
|
||||
console.log('长按触发,准备进入拖拽模式')
|
||||
}, 500) // 500ms 后触发长按
|
||||
}, [performLongPress])
|
||||
@@ -804,7 +804,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
// 🆕 处理长按拖拽逻辑 - 优化为连续手势
|
||||
// 处理长按拖拽逻辑 - 优化为连续手势
|
||||
if (isLongPressTriggered && !isLongPressDragging) {
|
||||
// 长按已触发,用户开始拖拽,进入长按拖拽模式
|
||||
if (!webSocket || !device || !operationEnabled) return
|
||||
@@ -819,17 +819,17 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
setIsLongPressDragging(true)
|
||||
setLongPressDragStartPos(startCoords)
|
||||
|
||||
// 🔧 优化:初始化拖拽路径,包含起始点
|
||||
// 优化:初始化拖拽路径,包含起始点
|
||||
setDragPath([startCoords])
|
||||
|
||||
// 🆕 显示长按拖拽开始指示器
|
||||
// 显示长按拖拽开始指示器
|
||||
if (screenDisplay.showTouchIndicator) {
|
||||
showLongPressDragStartIndicator(dragStart.x, dragStart.y)
|
||||
}
|
||||
|
||||
console.log(`长按拖拽开始: Device(${startCoords.x.toFixed(1)}, ${startCoords.y.toFixed(1)})`)
|
||||
} else if (isLongPressDragging) {
|
||||
// 🔧 优化:收集拖拽路径点,而不是立即发送消息
|
||||
// 优化:收集拖拽路径点,而不是立即发送消息
|
||||
const now = Date.now()
|
||||
|
||||
// 频率控制:每50ms最多记录一个点,避免路径过密
|
||||
@@ -863,16 +863,16 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
event.preventDefault()
|
||||
setIsDragging(false)
|
||||
|
||||
// 🆕 清理长按计时器
|
||||
// 清理长按计时器
|
||||
if (longPressTimerRef.current) {
|
||||
clearTimeout(longPressTimerRef.current)
|
||||
longPressTimerRef.current = null
|
||||
}
|
||||
|
||||
// 🆕 处理长按相关操作
|
||||
// 处理长按相关操作
|
||||
if (isLongPressTriggered) {
|
||||
if (isLongPressDragging) {
|
||||
// 🔧 优化:执行完整的长按拖拽手势
|
||||
// 优化:执行完整的长按拖拽手势
|
||||
if (webSocket && device && operationEnabled && longPressDragStartPos && dragPath.length > 0) {
|
||||
const canvas = canvasRef.current
|
||||
if (canvas) {
|
||||
@@ -902,7 +902,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
|
||||
console.log(`长按拖拽完成: Device(${completePath[0].x.toFixed(1)}, ${completePath[0].y.toFixed(1)}) -> Device(${endCoords.x.toFixed(1)}, ${endCoords.y.toFixed(1)}), 路径点数: ${completePath.length}`)
|
||||
|
||||
// 🆕 显示长按拖拽路径指示器
|
||||
// 显示长按拖拽路径指示器
|
||||
if (screenDisplay.showTouchIndicator) {
|
||||
showLongPressDragPath(dragStart.x, dragStart.y, endX, endY)
|
||||
}
|
||||
@@ -925,7 +925,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
setIsLongPressTriggered(false)
|
||||
setIsLongPressDragging(false)
|
||||
setLongPressDragStartPos(null)
|
||||
setDragPath([]) // 🔧 清理拖拽路径
|
||||
setDragPath([]) // 清理拖拽路径
|
||||
return
|
||||
}
|
||||
|
||||
@@ -991,20 +991,20 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
setIsDragging(false)
|
||||
setDragStart(null)
|
||||
|
||||
// 🆕 清理长按计时器和状态
|
||||
// 清理长按计时器和状态
|
||||
if (longPressTimerRef.current) {
|
||||
clearTimeout(longPressTimerRef.current)
|
||||
longPressTimerRef.current = null
|
||||
}
|
||||
setIsLongPressTriggered(false)
|
||||
|
||||
// 🆕 清理长按拖拽相关状态
|
||||
// 清理长按拖拽相关状态
|
||||
setIsLongPressDragging(false)
|
||||
setLongPressDragStartPos(null)
|
||||
setDragPath([]) // 🔧 清理拖拽路径
|
||||
setDragPath([]) // 清理拖拽路径
|
||||
}, [])
|
||||
|
||||
// 🔍 全屏切换
|
||||
// 全屏切换
|
||||
const toggleFullscreen = useCallback(() => {
|
||||
const container = fullscreenContainerRef.current
|
||||
if (!container) return
|
||||
@@ -1120,7 +1120,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
border: '2px solid #ff4d4f',
|
||||
animation: 'pulse 2s infinite'
|
||||
}}>
|
||||
🔒 操作已禁用
|
||||
[LOCKED] 操作已禁用
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1158,7 +1158,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 📊 画质控制面板 + 🔍 全屏按钮 */}
|
||||
{/* 画质控制面板 + 全屏按钮 */}
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
bottom: '8px',
|
||||
@@ -1176,7 +1176,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
borderRadius: '3px',
|
||||
fontFamily: 'monospace',
|
||||
}}>
|
||||
{displayFps} FPS{networkStats.dropRate > 0.05 ? ` ⚠${(networkStats.dropRate * 100).toFixed(0)}%丢帧` : ''}
|
||||
{displayFps} FPS{networkStats.dropRate > 0.05 ? ` [!]${(networkStats.dropRate * 100).toFixed(0)}%丢帧` : ''}
|
||||
</span>
|
||||
<div style={{ position: 'relative' }}>
|
||||
<button
|
||||
@@ -1223,7 +1223,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* 🔍 全屏/退出全屏按钮 */}
|
||||
{/* 全屏/退出全屏按钮 */}
|
||||
<button
|
||||
onClick={toggleFullscreen}
|
||||
title={isFullscreen ? '退出全屏 (ESC)' : '全屏显示'}
|
||||
@@ -1238,7 +1238,7 @@ const DeviceScreen: React.FC<DeviceScreenProps> = ({ deviceId, onScreenSizeChang
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
{isFullscreen ? '⮌' : '⛶'}
|
||||
{isFullscreen ? 'X' : '[ ]'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user