| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- // 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<Path> 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<Path> _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();
- }
- }
- }
|