Files
nj/app/lib/features/editor/widgets/active_stroke_painter.dart
iven e07da7addb perf(app): 手写引擎性能优化 — 双层架构 + 光栅化缓存 + O(1) 点缓冲
性能优化:
- 新建 StrokeRasterCache: 已完成笔画光栅化为 ui.Image 合成位图
- 新建 CachedStrokesPainter: 每帧仅 drawImage,O(1) 开销
- 新建 ActiveStrokePainter: 仅渲染当前笔画,isComplete: false
- _currentPoints 改为可变缓冲区 + ValueNotifier 驱动,消除 O(N²) 列表拷贝
- 双层 Stack 架构: 已缓存层(不随指针移动重绘) + 实时层(仅当前笔画)

Bug 修复:
- 橡皮擦 saveLayer 合成: BlendMode.dstOut 在离屏缓冲区中正确工作
- pointsToOutline 新增 isComplete 参数: 实时绘制传 false,完成笔画传 true
- 模式切换不再销毁 HandwritingCanvas: IgnorePointer 替代 if/else 分支

架构改进:
- 提取 createPaintForStroke() 为顶层函数,供缓存和 Painter 共用
- 移除旧 StrokePainter 类,由双层 Painter 替代
- LayoutBuilder 跟踪画布尺寸,尺寸变化时缓存自动失效

文件变更:
- 新建 stroke_cache.dart (~210 行)
- 新建 cached_strokes_painter.dart (~35 行)
- 新建 active_stroke_painter.dart (~70 行)
- 重写 handwriting_canvas.dart (~300 行)
- 重构 stroke_renderer.dart (~185 行, 移除旧 Painter)
- 修改 editor_page.dart (IgnorePointer 模式切换)

验证: flutter analyze 0 error
2026-06-01 13:18:36 +08:00

73 lines
2.0 KiB
Dart
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.
// 当前笔画实时 Painter — 绘制正在绘制中的笔画
//
// 接收可变点缓冲区的直接引用 + 版本号驱动重绘。
// 每帧仅计算当前笔画的轮廓路径,不影响已完成笔画层。
// isComplete: false 让 perfect_freehand 对实时笔尖做端点平滑。
import 'package:flutter/widgets.dart';
import 'stroke_model.dart';
import 'stroke_renderer.dart';
/// 当前笔画实时 Painter
///
/// 由 ListenableBuilder 包裹,监听 ValueNotifier<int> _strokeVersion。
/// 每次 pointer move 递增 version触发此 Painter 重绘。
/// 仅渲染当前正在绘制的笔画,已完成笔画由 CachedStrokesPainter 处理。
class ActiveStrokePainter extends CustomPainter {
/// 当前笔画的采样点(直接引用可变缓冲区,不拷贝)
final List<StrokePoint> points;
/// 画笔类型
final BrushType brushType;
/// 画笔颜色CSS 十六进制)
final String color;
/// 画笔宽度
final double width;
/// 版本号,每次 pointer move 递增
final int version;
ActiveStrokePainter({
required this.points,
required this.brushType,
required this.color,
required this.width,
required this.version,
});
@override
void paint(Canvas canvas, Size size) {
if (points.length < 2) return;
// isComplete: false — 实时笔尖不做端点封口,视觉更自然
final outlinePoints = pointsToOutline(
points,
brushType,
width,
isComplete: false,
);
if (outlinePoints.isEmpty) return;
final path = buildStrokePath(outlinePoints);
// 构造临时 Stroke 用于获取 Paint
final stroke = Stroke(
id: '__active__',
points: points,
brushType: brushType,
color: color,
width: width,
);
final paint = createPaintForStroke(stroke);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant ActiveStrokePainter oldDelegate) {
return oldDelegate.version != version;
}
}