board_painter.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import 'package:flutter/material.dart';
  2. import 'package:gobang/models/chess.dart';
  3. import 'package:gobang/models/position.dart';
  4. import 'package:gobang/themes/app_colors.dart';
  5. import 'package:gobang/utils/constants.dart';
  6. /// 棋盘 + 棋子绘制;最后一手支持缩放入场与高亮光圈。
  7. class BoardPainter extends CustomPainter {
  8. BoardPainter({
  9. required this.pieces,
  10. required this.version,
  11. this.lastMoveScale = 1,
  12. this.lastMovePulse = 0,
  13. });
  14. final List<Position> pieces;
  15. final int version;
  16. final double lastMoveScale;
  17. final double lastMovePulse;
  18. @override
  19. void paint(Canvas canvas, Size size) {
  20. final cell = size.width / (kBoardSize - 1);
  21. final bg = Paint()
  22. ..shader = const LinearGradient(
  23. begin: Alignment.topLeft,
  24. end: Alignment.bottomRight,
  25. colors: [
  26. AppColors.woodLight,
  27. AppColors.woodMid,
  28. AppColors.woodDark,
  29. ],
  30. ).createShader(Offset.zero & size);
  31. final rrect = RRect.fromRectAndRadius(
  32. Offset.zero & size,
  33. const Radius.circular(12),
  34. );
  35. canvas.drawRRect(rrect, bg);
  36. final line = Paint()
  37. ..style = PaintingStyle.stroke
  38. ..color = const Color(0x66FFFFFF)
  39. ..strokeWidth = 1;
  40. for (var i = 0; i < kBoardSize; i++) {
  41. final o = cell * i;
  42. canvas.drawLine(Offset(0, o), Offset(size.width, o), line);
  43. canvas.drawLine(Offset(o, 0), Offset(o, size.height), line);
  44. }
  45. // 星位
  46. final star = Paint()..color = const Color(0xFF3D2B1F);
  47. for (final p in const [
  48. Offset(3, 3),
  49. Offset(3, 11),
  50. Offset(7, 7),
  51. Offset(11, 3),
  52. Offset(11, 11),
  53. ]) {
  54. canvas.drawCircle(
  55. Offset(p.dx * cell, p.dy * cell),
  56. 3,
  57. star,
  58. );
  59. }
  60. final radius = cell / 2 - 2;
  61. final fill = Paint()..style = PaintingStyle.fill;
  62. final lastIndex = pieces.length - 1;
  63. for (var i = 0; i < pieces.length; i++) {
  64. final p = pieces[i];
  65. final isLast = i == lastIndex;
  66. final center = Offset(p.dx, p.dy);
  67. final scale = isLast ? lastMoveScale.clamp(0.0, 1.2) : 1.0;
  68. final r = radius * scale;
  69. if (isLast && lastMovePulse > 0) {
  70. final ring = Paint()
  71. ..style = PaintingStyle.stroke
  72. ..strokeWidth = 2.5
  73. ..color = AppColors.lastMark.withValues(
  74. alpha: 0.85 * lastMovePulse,
  75. );
  76. canvas.drawCircle(center, radius * (1.15 + 0.45 * lastMovePulse), ring);
  77. }
  78. // 棋子阴影
  79. canvas.drawCircle(
  80. center.translate(1.2, 1.8),
  81. r,
  82. Paint()..color = const Color(0x44000000),
  83. );
  84. final isWhite = p.chess is WhiteChess;
  85. fill.shader = RadialGradient(
  86. center: const Alignment(-0.35, -0.4),
  87. radius: 0.95,
  88. colors: isWhite
  89. ? const [Color(0xFFFFFFFF), Color(0xFFD8D8D8)]
  90. : const [Color(0xFF4A4A4A), Color(0xFF111111)],
  91. ).createShader(Rect.fromCircle(center: center, radius: r));
  92. if (p.chessShape.shape == 1) {
  93. canvas.drawCircle(center, r, fill);
  94. } else {
  95. canvas.drawRRect(
  96. RRect.fromRectAndRadius(
  97. Rect.fromCircle(center: center, radius: r),
  98. Radius.circular(r * 0.2),
  99. ),
  100. fill,
  101. );
  102. }
  103. fill.shader = null;
  104. if (isLast && scale > 0.85) {
  105. final mark = Paint()
  106. ..style = PaintingStyle.fill
  107. ..color = AppColors.lastMark;
  108. final s = radius * 0.28;
  109. final path = Path()
  110. ..moveTo(center.dx, center.dy - s)
  111. ..lineTo(center.dx - s * 0.9, center.dy + s * 0.7)
  112. ..lineTo(center.dx + s * 0.9, center.dy + s * 0.7)
  113. ..close();
  114. canvas.drawPath(path, mark);
  115. }
  116. }
  117. }
  118. @override
  119. bool shouldRepaint(covariant BoardPainter oldDelegate) {
  120. return oldDelegate.version != version ||
  121. oldDelegate.lastMoveScale != lastMoveScale ||
  122. oldDelegate.lastMovePulse != lastMovePulse;
  123. }
  124. }