Files
TrueGrowth/docs/DRAWING_HAND_PANNING_LESSONS.md

116 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 绘制工具手形平移经验
更新日期2026-05-03
## 背景
画笔、钢笔、橡皮擦处于绘制过程中时,用户仍然期望可以临时移动画布:
- 按住空格键拖动画布。
- 按鼠标中键拖动画布。
这类交互属于画布导航能力,优先级应高于当前绘制工具的落点行为。否则用户想移动视图时,会误画线、误加钢笔锚点或误擦除元素。
## 根因
Plait 底层的 `withHandPointer` 已经支持两类平移入口:
- 空格键会让画布容器进入 `viewport-moving` 状态。
- 鼠标中键通过 `isWheelPointer(event)` 进入手形移动。
业务绘制插件位于 `withHandPointer` 外层,会先收到 `pointerDown`。如果绘制插件在中键或 `viewport-moving` 状态下直接启动绘制/擦除,就会抢走底层手形平移事件。
这次暴露出三个层面的缺口:
1. 画笔、钢笔已有空格临时手形的透传逻辑,但没有显式覆盖中键。
2. 橡皮擦缺少临时手形透传逻辑,空格和中键都可能误进入擦除。
3. 物理 Pencil 模式会屏蔽非 Pencil 事件,也会把鼠标中键平移挡掉。
## 修复原则
绘制插件不要自己实现滚动或平移。
更稳的做法是:在绘制工具的 `pointerDown` 入口先判断当前事件是否应该交给手形平移插件;如果是,只把事件下放到底层 handler并在本插件内标记临时平移状态后续 `pointerMove``pointerUp` 继续透传。
共享判断应收口到一个小工具函数,避免画笔、钢笔、橡皮擦各自复制条件:
```ts
export const shouldDelegateToHandPointer = (
board: PlaitBoard,
event: PointerEvent
) => {
return isTemporaryHandMode(board) || isWheelPointer(event);
};
```
## 实现经验
### 1. 空格和中键要在同一层判断
不要只判断 `viewport-moving`
空格键在 `pointerDown` 前通常已经给容器加上 `viewport-moving`,所以绘制插件能识别;中键则要先把 `pointerDown` 交给底层 `withHandPointer`,底层才会设置中键移动状态。
因此绘制插件入口必须同时识别:
- `PlaitBoard.getBoardContainer(board).classList.contains('viewport-moving')`
- `isWheelPointer(event)`
### 2. 临时平移期间不要完成绘制动作
临时平移的 `pointerUp` 只应透传到底层,不应调用绘制完成逻辑:
- 画笔不插入 `Freehand`
- 钢笔不新增锚点、不完成路径。
- 橡皮擦不调用删除或精确擦除。
`globalPointerUp` 要清理本插件的临时平移状态,避免指针在画布外抬起后状态残留。
### 3. Pencil 模式仍要放行画布导航
物理 Pencil 模式屏蔽鼠标绘制是合理的,但画布导航不是绘制输入。中键和空格临时手形应绕过 `isPencilMode && !isPencilEvent` 的屏蔽逻辑。
判断顺序建议:
1. Pencil 事件可进入 Pencil 模式。
2. 普通鼠标事件默认仍屏蔽。
3. 中键或临时手形事件必须放行到底层。
### 4. 测试要覆盖“没有副作用”
这类修复不能只断言底层 handler 被调用,还要断言绘制副作用没有发生:
- 画笔中键/空格平移时不调用 `Transforms.insertNode`
- 橡皮擦中键/空格平移时不调用 `CoreTransforms.removeElements`
- 钢笔中键平移时不新增锚点、不插入路径。
- Pencil 模式下普通鼠标仍被屏蔽,中键和临时手形仍放行。
## 验证清单
- 画笔绘制过程中,按住空格拖动画布,不产生新笔画。
- 画笔绘制过程中,按鼠标中键拖动画布,不产生新笔画。
- 钢笔绘制过程中,按住空格拖动画布,不新增锚点。
- 钢笔绘制过程中,按鼠标中键拖动画布,不新增锚点。
- 橡皮擦过程中,按住空格拖动画布,不删除元素。
- 橡皮擦过程中,按鼠标中键拖动画布,不删除元素。
- 物理 Pencil 模式下,普通鼠标仍不能绘制,中键/空格仍能移动画布。
## 相关文件
- `packages/drawnix/src/plugins/hand-mode.ts`
- `packages/drawnix/src/plugins/freehand/with-freehand-create.ts`
- `packages/drawnix/src/plugins/freehand/with-freehand-erase.ts`
- `packages/drawnix/src/plugins/pen/with-pen-create.ts`
- `packages/drawnix/src/plugins/with-pencil.ts`
- `packages/drawnix/src/plugins/freehand/__tests__/hand-mode.test.ts`
- `packages/drawnix/src/plugins/pen/__tests__/with-pen-create.test.ts`
- `packages/drawnix/src/plugins/with-pencil.test.ts`
## 建议验证命令
```bash
pnpm exec vitest run src/plugins/freehand/__tests__/hand-mode.test.ts src/plugins/pen/__tests__/with-pen-create.test.ts src/plugins/with-pencil.test.ts --config vitest.config.ts
pnpm exec tsc -p tsconfig.lib.json --noEmit
```