From c0225d63ada664760464a3a015e0e0ef5bb60012 Mon Sep 17 00:00:00 2001 From: Calcitem Date: Sun, 30 May 2021 01:41:25 +0800 Subject: [PATCH] flutter: Support piece animation setting --- .../lib/painting/pieces_painter.dart | 53 ++++++++++++++----- src/ui/flutter_app/lib/widgets/board.dart | 8 ++- src/ui/flutter_app/lib/widgets/game_page.dart | 44 ++++++++++++++- 3 files changed, 89 insertions(+), 16 deletions(-) diff --git a/src/ui/flutter_app/lib/painting/pieces_painter.dart b/src/ui/flutter_app/lib/painting/pieces_painter.dart index 4213edda..99c1c148 100644 --- a/src/ui/flutter_app/lib/painting/pieces_painter.dart +++ b/src/ui/flutter_app/lib/painting/pieces_painter.dart @@ -24,27 +24,32 @@ import 'package:sanmill/style/app_theme.dart'; import 'painter_base.dart'; -class PiecePaintPair { +class PiecePaintParam { // TODO: null-safety final String? piece; final Offset? pos; - PiecePaintPair({this.piece, this.pos}); + final bool? animated; + PiecePaintParam({this.piece, this.pos, this.animated}); } class PiecesPainter extends PiecesBasePainter { final Position? position; final int? focusIndex, blurIndex; + final animationValue; // TODO: null-safety double? pieceWidth = 0.0; + double? animatedPieceWidth = 0.0; PiecesPainter({ required double width, required this.position, this.focusIndex = invalidIndex, this.blurIndex = invalidIndex, + required this.animationValue, }) : super(width: width) { pieceWidth = squareWidth * Config.pieceWidth; + animatedPieceWidth = squareWidth * Config.pieceWidth * animationValue; } @override @@ -56,6 +61,7 @@ class PiecesPainter extends PiecesBasePainter { gridWidth: gridWidth, squareWidth: squareWidth, pieceWidth: pieceWidth, + animatedPieceWidth: animatedPieceWidth, offsetX: AppTheme.boardPadding + squareWidth / 2, offsetY: AppTheme.boardPadding + squareWidth / 2, focusIndex: focusIndex, @@ -75,6 +81,7 @@ class PiecesPainter extends PiecesBasePainter { double? gridWidth, double? squareWidth, double? pieceWidth, + double? animatedPieceWidth, double? offsetX, double? offsetY, int? focusIndex = invalidIndex, @@ -85,7 +92,7 @@ class PiecesPainter extends PiecesBasePainter { final top = offsetY; final shadowPath = Path(); - final piecesToDraw = []; + final piecesToDraw = []; // TODO: null-safety Color? blurPositionColor; @@ -94,15 +101,16 @@ class PiecesPainter extends PiecesBasePainter { // Draw pieces on board for (var row = 0; row < 7; row++) { for (var col = 0; col < 7; col++) { - // - final piece = - position!.pieceOnGrid(row * 7 + col); // No Pieces when initial + final index = row * 7 + col; + final piece = position!.pieceOnGrid(index); // No Pieces when initial if (piece == Piece.noPiece) continue; var pos = Offset(left! + squareWidth! * col, top! + squareWidth * row); + var animated = (focusIndex == index); - piecesToDraw.add(PiecePaintPair(piece: piece, pos: pos)); + piecesToDraw + .add(PiecePaintParam(piece: piece, pos: pos, animated: animated)); shadowPath.addOval( Rect.fromCenter(center: pos, width: pieceWidth!, height: pieceWidth), @@ -127,20 +135,39 @@ class PiecesPainter extends PiecesBasePainter { var pieceRadius = pieceWidth! / 2; var pieceInnerRadius = pieceRadius * 0.99; + var animatedPieceRadius = animatedPieceWidth! / 2; + var animatedPieceInnerRadius = animatedPieceRadius * 0.99; + // Draw Border of Piece switch (pps.piece) { case Piece.whiteStone: paint.color = AppTheme.whitePieceBorderColor; - canvas.drawCircle(pps.pos!, pieceRadius, paint); // For debugging + canvas.drawCircle( + pps.pos!, + pps.animated! ? animatedPieceRadius : pieceRadius, + paint, + ); paint.color = Color(Config.whitePieceColor); - canvas.drawCircle(pps.pos!, pieceInnerRadius, paint); + canvas.drawCircle( + pps.pos!, + pps.animated! ? animatedPieceInnerRadius : pieceInnerRadius, + paint, + ); blurPositionColor = Color(Config.whitePieceColor).withOpacity(0.1); break; case Piece.blackStone: paint.color = AppTheme.blackPieceBorderColor; - canvas.drawCircle(pps.pos!, pieceRadius, paint); // For debugging + canvas.drawCircle( + pps.pos!, + pps.animated! ? animatedPieceRadius : pieceRadius, + paint, + ); paint.color = Color(Config.blackPieceColor); - canvas.drawCircle(pps.pos!, pieceInnerRadius, paint); + canvas.drawCircle( + pps.pos!, + pps.animated! ? animatedPieceInnerRadius : pieceInnerRadius, + paint, + ); blurPositionColor = Color(Config.blackPieceColor).withOpacity(0.1); break; case Piece.ban: @@ -178,7 +205,7 @@ class PiecesPainter extends PiecesBasePainter { canvas.drawCircle( Offset(left! + column * squareWidth!, top! + row * squareWidth), - pieceWidth! / 2, + animatedPieceWidth! / 2, paint, ); } @@ -191,7 +218,7 @@ class PiecesPainter extends PiecesBasePainter { canvas.drawCircle( Offset(left! + column * squareWidth!, top! + row * squareWidth), - pieceWidth! / 2 * 0.8, + animatedPieceWidth! / 2 * 0.8, paint, ); } diff --git a/src/ui/flutter_app/lib/widgets/board.dart b/src/ui/flutter_app/lib/widgets/board.dart index 51234596..0a693784 100644 --- a/src/ui/flutter_app/lib/widgets/board.dart +++ b/src/ui/flutter_app/lib/widgets/board.dart @@ -27,9 +27,14 @@ class Board extends StatelessWidget { final double width; final double height; final Function(BuildContext, int) onBoardTap; + final animationValue; final String tag = "[board]"; - Board({required this.width, required this.onBoardTap}) : height = width; + Board({ + required this.width, + required this.onBoardTap, + required this.animationValue, + }) : height = width; @override Widget build(BuildContext context) { @@ -49,6 +54,7 @@ class Board extends StatelessWidget { position: Game.instance.position, focusIndex: Game.instance.focusIndex, blurIndex: Game.instance.blurIndex, + animationValue: animationValue, ), child: container, ); diff --git a/src/ui/flutter_app/lib/widgets/game_page.dart b/src/ui/flutter_app/lib/widgets/game_page.dart index bbba9a34..246cb71d 100644 --- a/src/ui/flutter_app/lib/widgets/game_page.dart +++ b/src/ui/flutter_app/lib/widgets/game_page.dart @@ -55,7 +55,8 @@ class GamePage extends StatefulWidget { _GamePageState createState() => _GamePageState(); } -class _GamePageState extends State with RouteAware { +class _GamePageState extends State + with RouteAware, SingleTickerProviderStateMixin { String? _tip = ''; bool isReady = false; bool isGoingToHistory = false; @@ -64,6 +65,8 @@ class _GamePageState extends State with RouteAware { pixelRatio: 1.0, skipFramesBetweenCaptures: 0, ); + late AnimationController _animationController; + late Animation animation; final String tag = "[game_page]"; @override @@ -79,6 +82,8 @@ class _GamePageState extends State with RouteAware { timer = Timer.periodic(Duration(microseconds: 100), (Timer t) { _setReadyState(); }); + + _initAnimation(); } _setReadyState() async { @@ -95,6 +100,32 @@ class _GamePageState extends State with RouteAware { } } + _initAnimation() { + _animationController = AnimationController( + vsync: this, + duration: + Duration(milliseconds: (Config.animationDuration * 1000).toInt()), + ); + _animationController.addListener(() {}); + + animation = Tween( + begin: 1.27, // sqrt(1.618) = 1.272 + end: 1.0, + ).animate(_animationController) + ..addListener(() { + setState(() {}); + }); + + _animationController.addStatusListener((state) { + if (state == AnimationStatus.completed || + state == AnimationStatus.dismissed) { + _animationController.forward(); + } + }); + + _animationController.forward(); + } + showTip(String? tip) { if (!mounted) return; if (tip != null) print("[tip] $tip"); @@ -142,6 +173,10 @@ class _GamePageState extends State with RouteAware { return false; } + _animationController.duration = + Duration(milliseconds: (Config.animationDuration * 1000).toInt()); + _animationController.reset(); + if (Game.instance.engineType == EngineType.aiVsAi || Game.instance.engineType == EngineType.testViaLAN) { print("$tag Engine type is no human, ignore tapping."); @@ -448,6 +483,9 @@ class _GamePageState extends State with RouteAware { Move mv = response.value; final Move move = new Move(mv.move); + _animationController.duration = + Duration(milliseconds: (Config.animationDuration * 1000).toInt()); + _animationController.reset(); Game.instance.doMove(move.move); showTips(); break; @@ -1111,6 +1149,7 @@ class _GamePageState extends State with RouteAware { child: Board( width: boardWidth, onBoardTap: onBoardTap, + animationValue: animation.value, ), ); } @@ -1291,8 +1330,9 @@ class _GamePageState extends State with RouteAware { @override void dispose() { - print("$tag dipose"); + print("$tag dispose"); widget.engine.shutdown(); + _animationController.dispose(); super.dispose(); routeObserver.unsubscribe(this); }