board_painter.dart 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // board_painter.dart
  2. import 'dart:typed_data';
  3. import 'package:flutter/material.dart';
  4. import 'package:image_puzzle/play/board.dart';
  5. import 'package:image_puzzle/play/piece.dart';
  6. class BoardPainter extends CustomPainter {
  7. final Board board;
  8. // 碎片边框宽度 (可以比背景槽位稍粗,以便突出)
  9. static const double _pieceStrokeWidth = 1.0;
  10. // 边框画笔
  11. final Paint _pieceBorderPaint = Paint()
  12. ..color = Colors
  13. .black // 碎片边框颜色
  14. ..style = PaintingStyle.stroke
  15. ..strokeWidth = _pieceStrokeWidth
  16. ..isAntiAlias = true;
  17. // 边框画笔
  18. final Paint _innerPieceBorderPaint = Paint()
  19. ..color = Colors
  20. .white // 碎片边框颜色
  21. ..style = PaintingStyle.stroke
  22. ..strokeWidth = _pieceStrokeWidth
  23. ..isAntiAlias = true;
  24. BoardPainter({required this.board}) : super(repaint: Listenable.merge([board.boardNotifier])); // 触发重绘
  25. @override
  26. void paint(Canvas canvas, Size size) {
  27. // 绘制背景
  28. // _paintBackground(canvas, size);
  29. if (board.backgroundPicture != null) {
  30. canvas.drawPicture(board.backgroundPicture!);
  31. }
  32. // 绘制pieces
  33. for (final piece in board.pieces) {
  34. _drawPiece(canvas, size, piece);
  35. }
  36. }
  37. @override
  38. bool shouldRepaint(covariant BoardPainter oldDelegate) {
  39. return true;
  40. }
  41. void _drawPiece(Canvas canvas, Size size, Piece piece) {
  42. final double w = board.pieceLogicalWidth;
  43. final double h = board.pieceLogicalHeight;
  44. // 1. 准备变换矩阵和动态 Path
  45. final Float64List storage64 = Float64List.fromList(piece.transform.storage);
  46. // 核心:根据 borders 状态动态生成 Path
  47. final List<Path> paths = _getPiecePath(piece);
  48. final Path piecePath = paths[0];
  49. final Path outerBorderPath = paths[1];
  50. final Path innerBorderPath = paths[2];
  51. canvas.save();
  52. canvas.transform(storage64); // 应用平移
  53. // 裁剪区域:使用动态生成的 Path 来裁剪图片,实现圆角/尖角混合
  54. canvas.clipPath(piecePath);
  55. // 绘制图片:只有落在 rrect 内部的部分图片会被绘制
  56. final Rect dstRect = Rect.fromLTWH(0, 0, w, h);
  57. canvas.drawImageRect(board.image, piece.sourceRect, dstRect, Paint()..isAntiAlias = true);
  58. canvas.restore(); // 恢复 Canvas 状态 (移除裁剪和 transform)
  59. // --- 绘制边框 ---
  60. // 为了确保边框线宽向外延伸的部分不会被裁剪,我们需要在裁剪区域被移除后重新绘制。
  61. canvas.save();
  62. canvas.transform(storage64); // 重新应用平移
  63. // 绘制碎片边框
  64. canvas.drawPath(outerBorderPath, _pieceBorderPaint); // 外边框,黑色
  65. canvas.drawPath(innerBorderPath, _innerPieceBorderPaint); // 内边框,白色
  66. canvas.restore();
  67. }
  68. List<Path> _getPiecePath(Piece piece) {
  69. if (piece.path == null || piece.outLinePath == null || piece.innerLinePath == null) {
  70. return piece.generatePaths();
  71. } else {
  72. return [piece.path!, piece.outLinePath!, piece.innerLinePath!];
  73. }
  74. }
  75. // 绘制背景(已经被drawPicture取代)
  76. void _paintBackground(Canvas canvas, Size size) {
  77. // 绘制整个背景
  78. canvas.drawRect(
  79. Rect.fromLTWH(0, 0, size.width, size.height),
  80. Paint()
  81. ..color = Colors.lightGreen
  82. ..style = PaintingStyle.fill,
  83. );
  84. for (final piece in board.pieces) {
  85. canvas.save();
  86. // 转换 Matrix4 存储格式 (Float32List -> Float64List)
  87. final Float64List storage64 = Float64List.fromList(piece.transform.storage);
  88. // 应用碎片的几何变换 (平移/旋转/缩放)
  89. // 这将 Canvas 原点移动到 piece.transform.storage[12] 和 [13] 处。
  90. canvas.transform(storage64);
  91. final Rect rect = Rect.fromLTWH(0, 0, board.pieceLogicalWidth, board.pieceLogicalHeight);
  92. final RRect rrect = RRect.fromRectAndRadius(rect, Radius.circular(8.0));
  93. canvas.drawRRect(
  94. rrect,
  95. Paint()
  96. ..color = Colors.green
  97. ..style = PaintingStyle.fill,
  98. );
  99. canvas.drawRRect(
  100. rrect,
  101. Paint()
  102. ..color = Colors.blueGrey
  103. ..style = PaintingStyle.stroke
  104. ..strokeWidth = 1.0,
  105. );
  106. canvas.restore();
  107. }
  108. }
  109. }