原生鸿蒙像素画板实战 22:快捷键与鼠标交互
手机像素画板的核心输入是触控大屏和二合一设备却能提供键盘、鼠标和滚轮。把桌面快捷键原封不动搬到手机上只会让界面多出一套无用设置反过来完全忽略键盘又会让大屏编辑效率很低。bitArt 的处理方式是把快捷键配置持久化但只在大屏布局且开关允许时响应鼠标滚轮则仍归到画布视口逻辑围绕当前光标缩放。快捷键配置可以保存但响应范围必须受布局约束快捷键实现最常见的问题有两个。第一直接在页面里判断 keyCode随着撤销、导出、工具切换和弹窗关闭不断增加代码很快无法维护第二在设置弹窗、项目命名输入框或素材库打开时仍响应 B、E、S 等单键用户输入文本会意外切换工具或触发导出。按键解析和命令执行为什么要拆开键盘和鼠标应该增强现有工作流而不是制造另一套编辑器状态。所有输入最终仍要回到同一份工具、历史和视口规则。按键先解析成动作再决定怎样执行KeyboardShortcutService 维护动作列表、默认绑定、格式化显示、冲突移除和查找。EditorShortcutActionService 则负责根据 KeyEvent 更新 Ctrl、Shift 状态判断是否处于大屏、是否正在录制快捷键、是否允许单键再返回 actionId。真正的执行交给工作流工具动作映射到 activeTool缩放动作映射到视口关闭动作按叠层优先级处理。默认方案既支持 CtrlZ、CtrlY、CtrlShiftZ 这类组合也支持 B、E、F、P、M 等工具键。冲突分配时 withAssignedShortcut 会从其他动作移除相同绑定避免一个按键同时触发两个命令。默认快捷键只是配置数据真正是否响应还需要后续的布局和面板状态判断。static createDefaultShortcuts(): KeyboardShortcutSettings { const result: KeyboardShortcutSettings {}; result.undo [KeyboardShortcutService.createBinding(KeyCode.KEYCODE_Z, true, false)]; result.redo [KeyboardShortcutService.createBinding(KeyCode.KEYCODE_Y, true, false), KeyboardShortcutService.createBinding(KeyCode.KEYCODE_Z, true, true)]; result.pencil [KeyboardShortcutService.createBinding(KeyCode.KEYCODE_B, false, false)]; result.eraser [KeyboardShortcutService.createBinding(KeyCode.KEYCODE_E, false, false)]; result.fill [KeyboardShortcutService.createBinding(KeyCode.KEYCODE_F, false, false)]; result.closeOverlay [KeyboardShortcutService.createBinding(KeyCode.KEYCODE_ESCAPE, false, false)]; return result; }单键快捷键要避开正在输入文字的面板按键处理先更新修饰键状态再忽略仅有 Ctrl 或 Shift 的事件随后根据事件和回退状态判断组合键。若正在录制快捷键普通按键会被转成新的 binding否则用 findActionId 搜索匹配项。对于不带 Ctrl 与 Shift 的动作还要调用 canUseSingleKeyShortcuts确保没有新建、项目管理、设置、颜色、主菜单、历史或素材面板处于打开状态。if (!EditorShortcutActionService.isLargeScreenShortcutEnabled(state.largeScreenLayout, state.keyboardShortcutsEnabled)) { const result EditorShortcutActionService.createResult(state, false); result.shortcutCtrlDown false; result.shortcutShiftDown false; return result; } const actionId KeyboardShortcutService.findActionId(nextState.keyboardShortcuts, event.keyCode, ctrlPressed, shiftPressed); if (!ctrlPressed !shiftPressed actionId ! closeOverlay !EditorShortcutActionService.canUseSingleKeyShortcuts(nextState)) return createResult(nextState, false);输入会话状态与快捷键配置分开保存快捷键状态包含开关、绑定表、正在编辑的动作和修饰键按下状态。它是项目可保存配置但当前 Ctrl/Shift 按下状态是瞬态 UI 状态不能持久化。大屏条件不满足时服务返回未处理结果同时清理修饰键状态手机界面不会因为外接键盘残留状态而出现异常。快捷键绑定可以随项目保存按下 Ctrl 或 Shift 则只属于当前输入会话离开事件后不应残留。static withAssignedShortcut(settings: KeyboardShortcutSettings, actionId: string, binding: KeyboardShortcutBinding): KeyboardShortcutSettings { const normalized KeyboardShortcutService.normalizeShortcuts(settings); const result: KeyboardShortcutSettings {}; for (const currentActionId of KeyboardShortcutService.ACTIONS.map((item) item.id)) { const sourceBindings normalized[currentActionId] ? normalized[currentActionId] : []; result[currentActionId] currentActionId actionId ? [KeyboardShortcutService.cloneBinding(binding)] : KeyboardShortcutService.removeMatchingBindings(sourceBindings, binding); } return result; }大屏输入最容易和文本编辑冲突的情况不要在所有窗口尺寸下响应单键快捷键手机和文本输入面板必须有明确保护。重新绑定一个按键时要从其他动作移除冲突不能让查找顺序决定谁“碰巧获胜”。Ctrl、Shift 等修饰键需要有回退状态不能只依赖某些设备不一定提供的事件查询接口。鼠标和键盘不是另一套编辑器。它们最终仍调用已有的工具状态、撤销历史和视口约束。这样用户用 B 切到画笔、用滚轮缩放、用 Esc 关闭选区时不会走出触控路径之外的分叉逻辑。输入方式增加了数据源和业务规则没有增加。键盘输入先尊重当前焦点快捷键测试要把焦点场景列在第一位。先在画布空闲状态按 B、E、F、CtrlZ再打开项目重命名、设置输入框、颜色输入框和素材面板重复按同一组键。正确结果不是所有地方都响应而是只有没有文本输入和覆盖层的工作区响应单键。Esc 可以按叠层优先级关闭当前最高层界面但也不能越过用户正在编辑的重要输入。鼠标滚轮还要与快捷键分开验证。它不应因为某个快捷键被禁用就失效而应始终由视口判断当前缩放和边界。在大屏用光标指向一处像素后滚动目标像素应尽量留在指针下方切回手机布局时键盘动作停止触控和视口规则仍保持原有行为。这样多输入设备共存时才不会出现一方覆盖另一方的意外。怎样确认快捷键只在正确场景响应快捷键检查需要在面板打开和布局切换时重复按键确认保护条件真的生效。在大屏布局下验证 CtrlZ、CtrlY、CtrlShiftZ、保存、导出和常用工具键。打开设置、项目管理、颜色弹窗、素材库和文本输入后按 B、E、S确认不会误触发单键命令。重新给两个动作分配相同按键确认旧动作的绑定被移除且格式化显示正确。切换到手机布局或关闭快捷键开关确认按键不再修改编辑器状态滚轮缩放仍受视口边界约束。快捷键与鼠标检查记录 大屏开启时组合键与工具键可用 输入和弹窗打开时单键不误触发 重新绑定自动去除冲突 手机布局或关闭开关时快捷键保持无副作用快捷键功能的使用边界当前快捷键覆盖编辑器主动作不尝试复刻桌面绘图软件的全部组合键、宏录制或系统级全局热键。它的目标是在键盘可用的大屏上减少常见操作的重复点击同时不破坏手机上的触控主路径。这一篇的重点是把键盘和鼠标接入现有编辑器规则而不是另起一套行为。至此11 到 22 篇补齐了工具、项目、素材和大屏输入这些支线后续若继续扩展更适合围绕新能力或真实设备回归再开新的系列。