// board_painter.dart import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:image_puzzle/play/board.dart'; import 'package:image_puzzle/play/piece.dart'; class BoardPainter extends CustomPainter { final Board board; // 碎片边框宽度 (可以比背景槽位稍粗,以便突出) static const double _pieceStrokeWidth = 1.0; // 边框画笔 final Paint _pieceBorderPaint = Paint() ..color = Colors .black // 碎片边框颜色 ..style = PaintingStyle.stroke ..strokeWidth = _pieceStrokeWidth ..isAntiAlias = true; // 边框画笔 final Paint _innerPieceBorderPaint = Paint() ..color = Colors .white // 碎片边框颜色 ..style = PaintingStyle.stroke ..strokeWidth = _pieceStrokeWidth ..isAntiAlias = true; BoardPainter({required this.board}) : super(repaint: Listenable.merge([board.boardNotifier])); // 触发重绘 @override void paint(Canvas canvas, Size size) { // 绘制背景 // _paintBackground(canvas, size); if (board.backgroundPicture != null) { canvas.drawPicture(board.backgroundPicture!); } // 绘制pieces for (final piece in board.pieces) { _drawPiece(canvas, size, piece); } } @override bool shouldRepaint(covariant BoardPainter oldDelegate) { return true; } void _drawPiece(Canvas canvas, Size size, Piece piece) { final double w = board.pieceLogicalWidth; final double h = board.pieceLogicalHeight; // 1. 准备变换矩阵和动态 Path final Float64List storage64 = Float64List.fromList(piece.transform.storage); // 核心:根据 borders 状态动态生成 Path final List paths = _getPiecePath(piece); final Path piecePath = paths[0]; final Path outerBorderPath = paths[1]; final Path innerBorderPath = paths[2]; canvas.save(); canvas.transform(storage64); // 应用平移 // 裁剪区域:使用动态生成的 Path 来裁剪图片,实现圆角/尖角混合 canvas.clipPath(piecePath); // 绘制图片:只有落在 rrect 内部的部分图片会被绘制 final Rect dstRect = Rect.fromLTWH(0, 0, w, h); canvas.drawImageRect(board.image, piece.sourceRect, dstRect, Paint()..isAntiAlias = true); canvas.restore(); // 恢复 Canvas 状态 (移除裁剪和 transform) // --- 绘制边框 --- // 为了确保边框线宽向外延伸的部分不会被裁剪,我们需要在裁剪区域被移除后重新绘制。 canvas.save(); canvas.transform(storage64); // 重新应用平移 // 绘制碎片边框 canvas.drawPath(outerBorderPath, _pieceBorderPaint); // 外边框,黑色 canvas.drawPath(innerBorderPath, _innerPieceBorderPaint); // 内边框,白色 canvas.restore(); } List _getPiecePath(Piece piece) { if (piece.path == null || piece.outLinePath == null || piece.innerLinePath == null) { return piece.generatePaths(); } else { return [piece.path!, piece.outLinePath!, piece.innerLinePath!]; } } // 绘制背景(已经被drawPicture取代) void _paintBackground(Canvas canvas, Size size) { // 绘制整个背景 canvas.drawRect( Rect.fromLTWH(0, 0, size.width, size.height), Paint() ..color = Colors.lightGreen ..style = PaintingStyle.fill, ); for (final piece in board.pieces) { canvas.save(); // 转换 Matrix4 存储格式 (Float32List -> Float64List) final Float64List storage64 = Float64List.fromList(piece.transform.storage); // 应用碎片的几何变换 (平移/旋转/缩放) // 这将 Canvas 原点移动到 piece.transform.storage[12] 和 [13] 处。 canvas.transform(storage64); final Rect rect = Rect.fromLTWH(0, 0, board.pieceLogicalWidth, board.pieceLogicalHeight); final RRect rrect = RRect.fromRectAndRadius(rect, Radius.circular(8.0)); canvas.drawRRect( rrect, Paint() ..color = Colors.green ..style = PaintingStyle.fill, ); canvas.drawRRect( rrect, Paint() ..color = Colors.blueGrey ..style = PaintingStyle.stroke ..strokeWidth = 1.0, ); canvas.restore(); } } }