Initial TrueGrowth source import
This commit is contained in:
115
docs/DRAWING_HAND_PANNING_LESSONS.md
Normal file
115
docs/DRAWING_HAND_PANNING_LESSONS.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# 绘制工具手形平移经验
|
||||
|
||||
更新日期: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
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user