从 home_screen 转场到 board_play 时出现若干个 Raster Jank 的主要原因:
_recordBackground() 和 _recordCard() 在 Board 构造函数中同步执行generatePaths() 生成复杂的圆角路径_initAnimations() 中创建了 7 个 AnimationController// board.dart - Board 构造函数
Board(...) : finalRect = targetRect {
if (json != null) {
_restorePieces(json);
} else {
_initPieces();
}
rebuildAllGroups();
// 异步录制,不阻塞主线程
Future.microtask(() {
_recordBackground();
_recordCard();
});
}
效果:
// board_painter.dart - _drawPiece()
canvas.drawImageRect(
board.image,
piece.sourceRect,
dstRect,
Paint()
..isAntiAlias = true
..filterQuality = FilterQuality.low // 从默认的 medium 降低到 low
);
效果:
// board.dart - _recordBackground() 和 _recordCard()
final stopwatch = Stopwatch()..start();
// ... 录制逻辑 ...
stopwatch.stop();
_log.info('Picture recorded. Time: ${stopwatch.elapsedMilliseconds}ms');
效果:
# 运行 profile 模式并收集 Shader
flutter run --profile --cache-sksl --purge-persistent-cache
# 操作应用,触发所有可能的绘制场景
# 按 'M' 保存 Shader 缓存
# 构建时使用预热的 Shader
flutter build apk --bundle-sksl-path flutter_01.sksl.json
效果:
// board_play.dart - _initAnimations()
void _initAnimations() {
// 只初始化必要的动画
_moveAnimationController = AnimationController(...);
_mergeAnimationController = AnimationController(...);
_prepareAnimationController = AnimationController(...);
dealingAnimationController = AnimationController(...);
flipAnimationController = AnimationController(...);
// success 和 hardModeBanner 动画延迟到需要时再创建
// _successAnimationController = null;
// _hardModeBannerController = null;
}
// 在需要时才初始化
void _onSuccess() {
if (_successAnimationController == null) {
_initSuccessAnimation(device);
}
_successAnimationController!.forward(from: 0.0);
}
// board_play.dart - _buildPuzzleCanvas()
Widget _buildPuzzleCanvas(double width, double height) {
return RepaintBoundary( // 已经有了
child: CustomPaint(
painter: BoardPainter(board: board!, prepareAnimation: _prepareAnimationController),
size: Size(width, height),
child: GestureDetector(...),
),
);
}
当前状态:已经使用了 RepaintBoundary ✅
// piece.dart - Piece 类
class Piece {
// 当前已经有缓存机制
Path? path;
Path? innerLinePath;
Path? outLinePath;
List<Path> generatePaths({bool forceRecalculate = false}) {
// ✅ 已优化:如果没有 group 且路径已缓存,则直接返回
if (group == null && !forceRecalculate &&
path != null && outLinePath != null && innerLinePath != null) {
return [path!, outLinePath!, innerLinePath!];
}
// ... 生成逻辑
}
}
当前状态:已经实现了基本的缓存机制 ✅
对于非常复杂的 Path 生成(如不规则群组路径),可以考虑在 Isolate 中计算:
// 示例代码(未实施)
Future<Path> _generatePathInIsolate(PathGenerationParams params) async {
return await compute(_generatePathWorker, params);
}
static Path _generatePathWorker(PathGenerationParams params) {
// 在后台 Isolate 中生成 Path
return _generateIrregularGroupPath(...);
}
注意:Path 对象不能直接在 Isolate 间传递,需要序列化为坐标列表。
// board_play.dart - _init()
// 当前已经使用了 targetWidth 和 targetHeight 进行解码优化
final ui.Codec cardCodec = await ui.instantiateImageCodec(
cardData.buffer.asUint8List(),
targetWidth: bestCardImageSize.width.round(),
targetHeight: bestCardImageSize.height.round(),
);
当前状态:已经优化 ✅
flutter run --profile
# 打开 DevTools,查看 Performance 面板
# 重点关注:
# - Raster 线程的帧时间
# - Shader 编译事件
# - 图片解码事件
import 'dart:developer';
Timeline.startSync('RecordBackground');
_recordBackground();
Timeline.finishSync();
// 已经在代码中使用了 MemoryMonitor
MemoryMonitor.logMemoryUsage('BoardPlay initState');
实施上述优化后,预期可以:
当前已实施的优化主要针对:
建议优先实施:
通过这些优化,应该能够显著改善转场时的 Raster Jank 问题。