# 鼠标输入处理 **本文档引用的文件** - [inputremoting.js](file://client/src/inputremoting.js) - [inputdevice.js](file://client/src/inputdevice.js) - [mousebutton.js](file://client/src/mousebutton.js) - [pointercorrect.js](file://client/src/pointercorrect.js) - [sender.js](file://client/src/sender.js) - [memoryhelper.js](file://client/src/memoryhelper.js) - [register-events.js](file://client/public/videoplayer/js/register-events.js) - [main.js(视频播放器)](file://client/public/videoplayer/js/main.js) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考量](#性能考量) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件聚焦于鼠标输入处理的技术文档,涵盖鼠标事件的捕获、转换与转发机制,包括鼠标移动、点击、滚轮等事件类型。文档详细说明鼠标坐标系统、按钮状态映射与事件数据结构,并给出时间戳同步、坐标变换与延迟补偿策略。同时提供具体代码路径示例,展示如何捕获鼠标事件、序列化为二进制消息并通过网络传输,在远端设备上进行重放。 ## 项目结构 该仓库包含客户端与服务端两部分。与鼠标输入直接相关的核心代码位于 client/src 与 client/public/videoplayer/js 目录中: - 输入设备建模与事件序列化:client/src/inputdevice.js - 输入远程转发与消息封装:client/src/inputremoting.js - 鼠标按钮常量定义:client/src/mousebutton.js - 坐标矫正与视频适配:client/src/pointercorrect.js - 本地事件监听与状态队列:client/src/sender.js - 内存位写入工具:client/src/memoryhelper.js - 前端事件注册与消息发送(另一套实现):client/public/videoplayer/js/register-events.js - 视频播放器入口与事件注册:client/public/videoplayer/js/main.js ```mermaid graph TB subgraph "客户端" A["事件监听
sender.js"] B["输入设备模型
inputdevice.js"] C["远程转发与消息封装
inputremoting.js"] D["坐标矫正
pointercorrect.js"] E["内存位写入
memoryhelper.js"] F["鼠标按钮常量
mousebutton.js"] G["前端事件注册(替代实现)
register-events.js"] end A --> B A --> D A --> C B --> E B --> F G --> |"注册鼠标/滚轮/触摸事件"| A ``` 图表来源 - [sender.js:14-188](file://client/src/sender.js#L14-L188) - [inputdevice.js:91-272](file://client/src/inputdevice.js#L91-L272) - [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169) - [pointercorrect.js:6-124](file://client/src/pointercorrect.js#L6-L124) - [memoryhelper.js:1-28](file://client/src/memoryhelper.js#L1-L28) - [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7) - [register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281) 章节来源 - [sender.js:14-188](file://client/src/sender.js#L14-L188) - [inputdevice.js:91-272](file://client/src/inputdevice.js#L91-L272) - [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169) - [pointercorrect.js:6-124](file://client/src/pointercorrect.js#L6-L124) - [memoryhelper.js:1-28](file://client/src/memoryhelper.js#L1-L28) - [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7) - [register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281) ## 核心组件 - LocalInputManager:本地输入管理基类,负责事件分发与时间基准。 - Sender:具体实现,注册鼠标/键盘/手柄/触摸事件,构建输入设备状态,派发 StateEvent/TextEvent。 - Mouse/MouseState:鼠标设备与状态,包含位置、相对位移、滚轮、按钮位图等字段。 - InputRemoting:将本地事件序列化为消息并转发给订阅者(如 RTC 数据通道)。 - Message/NewEventsMsg/NewDeviceMsg:消息封装与序列化。 - PointerCorrector:将页面坐标映射到视频画布坐标,处理留边距与缩放。 - MemoryHelper:按位写入布尔状态,用于按钮位图。 - MouseButton:按钮枚举。 章节来源 - [inputremoting.js:9-49](file://client/src/inputremoting.js#L9-L49) - [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169) - [inputdevice.js:91-272](file://client/src/inputdevice.js#L91-L272) - [sender.js:14-188](file://client/src/sender.js#L14-L188) - [pointercorrect.js:6-124](file://client/src/pointercorrect.js#L6-L124) - [memoryhelper.js:1-28](file://client/src/memoryhelper.js#L1-L28) - [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7) ## 架构总览 鼠标事件从 DOM 事件开始,经由 Sender 注册监听,转换为 MouseState,再包装为 StateEvent,通过 InputRemoting 序列化为 Message 并发送至远端。 ```mermaid sequenceDiagram participant U as "用户" participant DOM as "DOM事件" participant S as "Sender" participant M as "Mouse/MouseState" participant PC as "PointerCorrector" participant IR as "InputRemoting" participant MSG as "Message/NewEventsMsg" participant NET as "网络/数据通道" U->>DOM : "鼠标移动/点击/滚轮" DOM->>S : "事件回调" S->>M : "queueEvent(event)" M-->>S : "currentState" S->>PC : "map(position)" PC-->>S : "矫正后坐标" S->>IR : "派发 StateEvent" IR->>MSG : "NewEventsMsg.create(stateEvent)" MSG-->>IR : "Message" IR->>NET : "发送 buffer" ``` 图表来源 - [sender.js:121-129](file://client/src/sender.js#L121-L129) - [inputdevice.js:91-98](file://client/src/inputdevice.js#L91-L98) - [inputdevice.js:195-272](file://client/src/inputdevice.js#L195-L272) - [pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40) - [inputremoting.js:138-141](file://client/src/inputremoting.js#L138-L141) - [inputremoting.js:258-277](file://client/src/inputremoting.js#L258-L277) ## 详细组件分析 ### 鼠标事件捕获与转换 - 事件监听:在视频元素上注册 click/mousedown/mouseup/mousemove/wheel 回调。 - 状态构建:将 MouseEvent/WheelEvent 转换为 MouseState,记录绝对坐标、相对位移、滚轮增量与按钮位图。 - 按钮映射:使用位图存储左右中及其他扩展按钮状态,通过 MemoryHelper.writeSingleBit 写入。 - 坐标矫正:使用 PointerCorrector 将页面坐标(clientX/Y)映射到视频画布坐标,考虑留边距与缩放。 ```mermaid flowchart TD Start(["事件到达"]) --> Type{"事件类型"} Type --> |mousemove| BuildMove["构建 MouseState
position/delta/buttons"] Type --> |click/mousedown/mouseup| BuildBtn["构建 MouseState
position/buttons"] Type --> |wheel| BuildWheel["构建 MouseState
scroll"] BuildMove --> BtnMap["按钮位图写入"] BuildBtn --> BtnMap BuildWheel --> BtnMap BtnMap --> Correct["PointerCorrector.map(position)"] Correct --> Queue["派发 StateEvent"] Queue --> End(["完成"]) ``` 图表来源 - [sender.js:121-129](file://client/src/sender.js#L121-L129) - [inputdevice.js:195-272](file://client/src/inputdevice.js#L195-L272) - [memoryhelper.js:7-20](file://client/src/memoryhelper.js#L7-L20) - [pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40) 章节来源 - [sender.js:31-49](file://client/src/sender.js#L31-L49) - [sender.js:121-129](file://client/src/sender.js#L121-L129) - [inputdevice.js:91-98](file://client/src/inputdevice.js#L91-L98) - [inputdevice.js:195-272](file://client/src/inputdevice.js#L195-L272) - [memoryhelper.js:7-20](file://client/src/memoryhelper.js#L7-L20) - [pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40) ### 鼠标坐标系统与按钮状态 - 坐标系统 - 绝对坐标:clientX/clientY 来源于 DOM 事件。 - 相对位移:movementX/movementY 由浏览器提供;Y 方向取负以匹配常见坐标系。 - 留边距与缩放:通过 PointerCorrector 的 contentRect 与 video 尺寸计算映射。 - 按钮状态 - 使用 16 字节位图存储多按钮状态,左/右/中/前进/后退分别对应不同位。 - 通过 MemoryHelper.writeSingleBit 写入位图。 章节来源 - [inputdevice.js:222-244](file://client/src/inputdevice.js#L222-L244) - [inputdevice.js:249-264](file://client/src/inputdevice.js#L249-L264) - [memoryhelper.js:7-20](file://client/src/memoryhelper.js#L7-L20) - [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7) ### 事件数据结构与时序 - InputEvent:统一事件头,包含类型、大小、设备 ID、时间戳。 - StateEvent:封装 IInputState,携带状态格式与状态数据。 - Message:封装 participant_id、type、length 与 data,作为网络传输单元。 - 时间戳:LocalInputManager 提供基于秒的时间基准,Sender 在派发时使用 timeSinceStartup。 ```mermaid classDiagram class InputEvent { +number type +number sizeInBytes +number deviceId +number time +buffer } class IInputState { +buffer +format } class MouseState { +float32 position[2] +float32 delta[2] +float32 scroll[2] +uint16 buttons +uint16 displayIndex +uint16 clickCount +buffer +format } class StateEvent { +baseEvent : InputEvent +stateFormat : number +stateData : ArrayBuffer +buffer } class Message { +number participant_id +number type +number length +ArrayBuffer data +buffer } MouseState ..|> IInputState StateEvent --> InputEvent : "组合" Message --> StateEvent : "承载" ``` 图表来源 - [inputdevice.js:129-178](file://client/src/inputdevice.js#L129-L178) - [inputdevice.js:180-193](file://client/src/inputdevice.js#L180-L193) - [inputdevice.js:195-272](file://client/src/inputdevice.js#L195-L272) - [inputremoting.js:184-232](file://client/src/inputremoting.js#L184-L232) 章节来源 - [inputdevice.js:129-178](file://client/src/inputdevice.js#L129-L178) - [inputdevice.js:180-193](file://client/src/inputdevice.js#L180-L193) - [inputdevice.js:195-272](file://client/src/inputdevice.js#L195-L272) - [inputremoting.js:184-232](file://client/src/inputremoting.js#L184-L232) ### 网络传输与远端重放 - 发送侧 - InputRemoting 订阅 LocalInputManager 的事件,将 StateEvent 包装为 NewEventsMsg,再封装为 Message。 - 通过订阅者(如 RTC 数据通道)发送二进制 buffer。 - 接收侧 - 远端需解析 Message,识别事件类型与状态格式,重建 MouseState 并应用到目标设备或渲染层。 - 由于未在仓库中发现接收与重放的具体实现,建议遵循以下原则: - 使用与发送端一致的格式标识与字段顺序。 - 使用时间戳进行本地重放缓冲与插值(见“性能考量”)。 ```mermaid sequenceDiagram participant L as "本地 Sender" participant IR as "InputRemoting" participant CH as "数据通道" participant R as "远端接收器" L->>IR : "StateEvent" IR->>IR : "NewEventsMsg.create()" IR->>CH : "Message.buffer" CH-->>R : "二进制数据" R->>R : "解析格式/重建状态" R-->>R : "重放到目标设备" ``` 图表来源 - [inputremoting.js:138-141](file://client/src/inputremoting.js#L138-L141) - [inputremoting.js:258-277](file://client/src/inputremoting.js#L258-L277) - [sender.js:170-182](file://client/src/sender.js#L170-L182) 章节来源 - [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169) - [inputremoting.js:184-232](file://client/src/inputremoting.js#L184-L232) - [sender.js:170-182](file://client/src/sender.js#L170-L182) ### 坐标变换与延迟补偿策略 - 坐标变换 - 将 clientX/Y 减去元素边界矩形偏移,反转 Y 轴,减去留边距,再按内容矩形与视频尺寸比例映射。 - 通过 PointerCorrector 的 contentRect 动态计算留边类型(水平/垂直)与尺寸。 - 延迟补偿 - 使用 timeSinceStartup 作为事件时间戳,远端可据此进行缓冲与插值,减少抖动。 - 若网络存在抖动,可在接收端维护一个环形缓冲队列,按时间戳排序后重放。 章节来源 - [pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40) - [pointercorrect.js:78-119](file://client/src/pointercorrect.js#L78-L119) - [sender.js:39-41](file://client/src/sender.js#L39-L41) - [sender.js:170-172](file://client/src/sender.js#L170-L172) ### 另一套前端事件注册实现(对比参考) - register-events.js 提供了另一种前端事件注册方式,同样处理鼠标、滚轮与触摸事件,并将坐标与按钮状态打包为二进制消息发送。 - 该实现与 sender.js 的差异在于: - 事件监听对象不同(视频元素 vs 文档/窗口)。 - 坐标映射逻辑略有差异(考虑 videoOriginX/Y 与 videoScale)。 - 未使用 InputRemoting/Message 体系,而是直接构造 DataView 发送。 章节来源 - [register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281) - [main.js(视频播放器):132-142](file://client/public/videoplayer/js/main.js#L132-L142) ## 依赖关系分析 - Sender 依赖 Mouse/MouseState、PointerCorrector、LocalInputManager、StateEvent。 - MouseState 依赖 MemoryHelper 与 MouseButton。 - InputRemoting 依赖 Message/NewEventsMsg/RemoveDeviceMsg/ChangeUsageMsg 与订阅者。 - register-events.js 与 main.js 形成独立的事件注册与发送链路。 ```mermaid graph LR Sender["sender.js"] --> Mouse["inputdevice.js: Mouse/MouseState"] Sender --> PC["pointercorrect.js"] Sender --> LIM["inputremoting.js: LocalInputManager"] Sender --> SE["inputdevice.js: StateEvent"] Mouse --> MH["memoryhelper.js"] Mouse --> MB["mousebutton.js"] IR["inputremoting.js"] --> MSG["inputremoting.js: Message/NewEventsMsg"] RE["register-events.js"] --> |"事件注册/发送"| Sender ``` 图表来源 - [sender.js:14-188](file://client/src/sender.js#L14-L188) - [inputdevice.js:91-272](file://client/src/inputdevice.js#L91-L272) - [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169) - [memoryhelper.js:1-28](file://client/src/memoryhelper.js#L1-L28) - [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7) - [register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281) 章节来源 - [sender.js:14-188](file://client/src/sender.js#L14-L188) - [inputdevice.js:91-272](file://client/src/inputdevice.js#L91-L272) - [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169) - [memoryhelper.js:1-28](file://client/src/memoryhelper.js#L1-L28) - [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7) - [register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281) ## 性能考量 - 事件采样与节流 - 对高频 mousemove 可采用节流/降采样策略,避免带宽与 CPU 压力。 - 时间戳与缓冲 - 使用 timeSinceStartup 作为单调递增时间戳,远端按时间戳排序与重放。 - 建议在接收端维护环形缓冲,按目标帧率进行插值。 - 坐标计算优化 - 将 contentRect 缓存并在 resize 时更新,避免重复计算。 - 位图写入 - 使用 MemoryHelper.writeSingleBit 批量写入按钮状态,减少内存拷贝。 ## 故障排查指南 - 事件未触发 - 检查事件监听是否绑定到正确的元素(视频元素)。 - 确认 PointerCorrector 的 videoWidth/videoHeight 已正确初始化。 - 坐标错位 - 核对 contentRect 与留边距计算,确认 videoScale 与 originX/Y 设置。 - 按钮状态异常 - 检查按钮位图写入逻辑,确认 MouseButton 枚举与位索引一致。 - 网络发送失败 - 确认订阅者 readyState 为 open,Message.buffer 构造无误。 章节来源 - [sender.js:114-120](file://client/src/sender.js#L114-L120) - [pointercorrect.js:71-76](file://client/src/pointercorrect.js#L71-L76) - [memoryhelper.js:7-20](file://client/src/memoryhelper.js#L7-L20) - [inputremoting.js:202-208](file://client/src/inputremoting.js#L202-L208) ## 结论 本项目提供了完整的鼠标输入捕获、转换与转发链路:DOM 事件 → MouseState → StateEvent → Message → 网络传输。通过 PointerCorrector 实现跨设备坐标一致性,MemoryHelper 提供高效的按钮位图写入。若需在远端重放,应严格遵循格式标识与字段顺序,并结合时间戳进行缓冲与插值,以获得流畅体验。 ## 附录 - 代码路径示例(不展示具体代码内容) - 鼠标事件监听与状态派发:[sender.js:121-129](file://client/src/sender.js#L121-L129) - MouseState 构建与位图写入:[inputdevice.js:222-244](file://client/src/inputdevice.js#L222-L244) - 坐标矫正:[pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40) - 事件序列化为 Message:[inputremoting.js:258-277](file://client/src/inputremoting.js#L258-L277) - 前端事件注册(对比实现):[register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281)