feat: PLANNING体系首版(00路线图/02任务总表/03执行协议/I-03/I-04/registry42口径)+M1止血交付
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
# 图片编辑器移动端适配
|
||||
|
||||
## 摘要
|
||||
为图片编辑器(ImageEditor)添加移动端触摸事件支持,使画笔和文本工具能在移动设备上使用。
|
||||
|
||||
## 当前状态分析
|
||||
|
||||
### 核心问题
|
||||
Canvas 元素只绑定了鼠标事件,未绑定触摸事件:
|
||||
|
||||
```tsx
|
||||
// 当前实现 - 仅支持鼠标
|
||||
<canvas
|
||||
onClick={handleCanvasClick} // 鼠标点击
|
||||
onMouseDown={handleMouseDown} // 鼠标按下
|
||||
onMouseMove={handleMouseMove} // 鼠标移动
|
||||
onMouseUp={handleMouseUp} // 鼠标抬起
|
||||
/>
|
||||
```
|
||||
|
||||
### 受影响功能
|
||||
- **画笔工具**: 无法在触摸屏上绘画
|
||||
- **文本工具**: 无法在触摸屏上点击放置文本
|
||||
|
||||
## 实施步骤
|
||||
|
||||
### 步骤 1:添加触摸坐标获取函数
|
||||
|
||||
在 ImageEditor.tsx 中添加通用的位置获取函数:
|
||||
|
||||
```tsx
|
||||
// 获取鼠标或触摸位置
|
||||
const getEventPosition = (e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>) => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return null;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
|
||||
let clientX: number, clientY: number;
|
||||
if ('touches' in e) {
|
||||
const touch = e.touches[0] || e.changedTouches[0];
|
||||
if (!touch) return null;
|
||||
clientX = touch.clientX;
|
||||
clientY = touch.clientY;
|
||||
} else {
|
||||
clientX = e.clientX;
|
||||
clientY = e.clientY;
|
||||
}
|
||||
|
||||
return {
|
||||
x: (clientX - rect.left) * scaleX,
|
||||
y: (clientY - rect.top) * scaleY,
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### 步骤 2:添加触摸事件处理函数
|
||||
|
||||
```tsx
|
||||
// 触摸开始
|
||||
const handleTouchStart = (e: React.TouchEvent<HTMLCanvasElement>) => {
|
||||
e.preventDefault();
|
||||
const pos = getEventPosition(e);
|
||||
if (!pos) return;
|
||||
|
||||
if (activeTool === "brush") {
|
||||
setIsDrawing(true);
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(pos.x, pos.y);
|
||||
ctx.strokeStyle = brushColor;
|
||||
ctx.lineWidth = brushSize;
|
||||
ctx.lineCap = "round";
|
||||
ctx.lineJoin = "round";
|
||||
}
|
||||
};
|
||||
|
||||
// 触摸移动
|
||||
const handleTouchMove = (e: React.TouchEvent<HTMLCanvasElement>) => {
|
||||
e.preventDefault();
|
||||
if (!isDrawing || activeTool !== "brush") return;
|
||||
const pos = getEventPosition(e);
|
||||
if (!pos) return;
|
||||
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
ctx.lineTo(pos.x, pos.y);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
// 触摸结束
|
||||
const handleTouchEnd = (e: React.TouchEvent<HTMLCanvasElement>) => {
|
||||
e.preventDefault();
|
||||
if (isDrawing) {
|
||||
setIsDrawing(false);
|
||||
saveHistory();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 步骤 3:修改文本工具点击处理
|
||||
|
||||
```tsx
|
||||
const handleCanvasTouchTap = (e: React.TouchEvent<HTMLCanvasElement>) => {
|
||||
e.preventDefault();
|
||||
if (activeTool !== "text" || !textInput.trim()) return;
|
||||
const pos = getEventPosition(e);
|
||||
if (!pos) return;
|
||||
|
||||
const newText: TextItem = {
|
||||
id: Math.random().toString(36).substring(2, 9),
|
||||
text: textInput,
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
fontSize: textSize,
|
||||
color: textColor,
|
||||
};
|
||||
setTexts((prev) => [...prev, newText]);
|
||||
saveHistory();
|
||||
};
|
||||
```
|
||||
|
||||
### 步骤 4:更新 Canvas JSX
|
||||
|
||||
```tsx
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className={`ie-canvas ${activeTool === "text" ? "text-cursor" : ""} ${activeTool === "brush" ? "brush-cursor" : ""}`}
|
||||
onClick={handleCanvasClick}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseLeave={handleMouseUp}
|
||||
onTouchStart={handleTouchStart}
|
||||
onTouchMove={handleTouchMove}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
onClick={activeTool === "text" ? handleCanvasClick : undefined}
|
||||
onTouchStart={activeTool === "text" ? handleCanvasTouchTap : handleTouchStart}
|
||||
/>
|
||||
```
|
||||
|
||||
### 步骤 5:添加 CSS 触摸优化
|
||||
|
||||
在 ImageEditor.css 中添加:
|
||||
|
||||
```css
|
||||
/* 防止浏览器默认触摸行为 */
|
||||
.ie-canvas {
|
||||
touch-action: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* 移动端画布区域优化 */
|
||||
@media (max-width: 768px) {
|
||||
.ie-canvas {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.ie-tool-btn {
|
||||
min-width: 48px;
|
||||
min-height: 48px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 涉及的文件
|
||||
|
||||
| 文件 | 操作 |
|
||||
|------|------|
|
||||
| `src/pages/ImageEditor/ImageEditor.tsx` | 添加触摸事件处理函数和位置获取函数 |
|
||||
| `src/pages/ImageEditor/ImageEditor.css` | 添加触摸优化样式 |
|
||||
|
||||
## 验证步骤
|
||||
|
||||
1. 运行 TypeScript 编译检查:`npx tsc --noEmit`
|
||||
2. 启动开发服务器:`npm run dev`
|
||||
3. 在移动设备或浏览器开发者工具中测试:
|
||||
- 画笔工具:手指在画布上滑动应该能绘制线条
|
||||
- 文本工具:手指点击画布应该能放置文本
|
||||
- 其他功能(亮度、滤镜等)应该正常工作
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 触摸事件需要 `e.preventDefault()` 阻止默认行为(如页面滚动)
|
||||
- `touch-action: none` 防止浏览器处理触摸手势
|
||||
- 坐标转换需要考虑 Canvas 的缩放比例
|
||||
- 触摸事件使用 `e.touches[0]` 获取第一个触摸点
|
||||
Reference in New Issue
Block a user