flutter: Support piece animation setting

This commit is contained in:
Calcitem 2021-05-30 01:41:25 +08:00
parent f32e9eb20c
commit c0225d63ad
3 changed files with 89 additions and 16 deletions

View File

@ -24,27 +24,32 @@ import 'package:sanmill/style/app_theme.dart';
import 'painter_base.dart'; import 'painter_base.dart';
class PiecePaintPair { class PiecePaintParam {
// TODO: null-safety // TODO: null-safety
final String? piece; final String? piece;
final Offset? pos; final Offset? pos;
PiecePaintPair({this.piece, this.pos}); final bool? animated;
PiecePaintParam({this.piece, this.pos, this.animated});
} }
class PiecesPainter extends PiecesBasePainter { class PiecesPainter extends PiecesBasePainter {
final Position? position; final Position? position;
final int? focusIndex, blurIndex; final int? focusIndex, blurIndex;
final animationValue;
// TODO: null-safety // TODO: null-safety
double? pieceWidth = 0.0; double? pieceWidth = 0.0;
double? animatedPieceWidth = 0.0;
PiecesPainter({ PiecesPainter({
required double width, required double width,
required this.position, required this.position,
this.focusIndex = invalidIndex, this.focusIndex = invalidIndex,
this.blurIndex = invalidIndex, this.blurIndex = invalidIndex,
required this.animationValue,
}) : super(width: width) { }) : super(width: width) {
pieceWidth = squareWidth * Config.pieceWidth; pieceWidth = squareWidth * Config.pieceWidth;
animatedPieceWidth = squareWidth * Config.pieceWidth * animationValue;
} }
@override @override
@ -56,6 +61,7 @@ class PiecesPainter extends PiecesBasePainter {
gridWidth: gridWidth, gridWidth: gridWidth,
squareWidth: squareWidth, squareWidth: squareWidth,
pieceWidth: pieceWidth, pieceWidth: pieceWidth,
animatedPieceWidth: animatedPieceWidth,
offsetX: AppTheme.boardPadding + squareWidth / 2, offsetX: AppTheme.boardPadding + squareWidth / 2,
offsetY: AppTheme.boardPadding + squareWidth / 2, offsetY: AppTheme.boardPadding + squareWidth / 2,
focusIndex: focusIndex, focusIndex: focusIndex,
@ -75,6 +81,7 @@ class PiecesPainter extends PiecesBasePainter {
double? gridWidth, double? gridWidth,
double? squareWidth, double? squareWidth,
double? pieceWidth, double? pieceWidth,
double? animatedPieceWidth,
double? offsetX, double? offsetX,
double? offsetY, double? offsetY,
int? focusIndex = invalidIndex, int? focusIndex = invalidIndex,
@ -85,7 +92,7 @@ class PiecesPainter extends PiecesBasePainter {
final top = offsetY; final top = offsetY;
final shadowPath = Path(); final shadowPath = Path();
final piecesToDraw = <PiecePaintPair>[]; final piecesToDraw = <PiecePaintParam>[];
// TODO: null-safety // TODO: null-safety
Color? blurPositionColor; Color? blurPositionColor;
@ -94,15 +101,16 @@ class PiecesPainter extends PiecesBasePainter {
// Draw pieces on board // Draw pieces on board
for (var row = 0; row < 7; row++) { for (var row = 0; row < 7; row++) {
for (var col = 0; col < 7; col++) { for (var col = 0; col < 7; col++) {
// final index = row * 7 + col;
final piece = final piece = position!.pieceOnGrid(index); // No Pieces when initial
position!.pieceOnGrid(row * 7 + col); // No Pieces when initial
if (piece == Piece.noPiece) continue; if (piece == Piece.noPiece) continue;
var pos = Offset(left! + squareWidth! * col, top! + squareWidth * row); 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( shadowPath.addOval(
Rect.fromCenter(center: pos, width: pieceWidth!, height: pieceWidth), Rect.fromCenter(center: pos, width: pieceWidth!, height: pieceWidth),
@ -127,20 +135,39 @@ class PiecesPainter extends PiecesBasePainter {
var pieceRadius = pieceWidth! / 2; var pieceRadius = pieceWidth! / 2;
var pieceInnerRadius = pieceRadius * 0.99; var pieceInnerRadius = pieceRadius * 0.99;
var animatedPieceRadius = animatedPieceWidth! / 2;
var animatedPieceInnerRadius = animatedPieceRadius * 0.99;
// Draw Border of Piece // Draw Border of Piece
switch (pps.piece) { switch (pps.piece) {
case Piece.whiteStone: case Piece.whiteStone:
paint.color = AppTheme.whitePieceBorderColor; 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); 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); blurPositionColor = Color(Config.whitePieceColor).withOpacity(0.1);
break; break;
case Piece.blackStone: case Piece.blackStone:
paint.color = AppTheme.blackPieceBorderColor; 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); 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); blurPositionColor = Color(Config.blackPieceColor).withOpacity(0.1);
break; break;
case Piece.ban: case Piece.ban:
@ -178,7 +205,7 @@ class PiecesPainter extends PiecesBasePainter {
canvas.drawCircle( canvas.drawCircle(
Offset(left! + column * squareWidth!, top! + row * squareWidth), Offset(left! + column * squareWidth!, top! + row * squareWidth),
pieceWidth! / 2, animatedPieceWidth! / 2,
paint, paint,
); );
} }
@ -191,7 +218,7 @@ class PiecesPainter extends PiecesBasePainter {
canvas.drawCircle( canvas.drawCircle(
Offset(left! + column * squareWidth!, top! + row * squareWidth), Offset(left! + column * squareWidth!, top! + row * squareWidth),
pieceWidth! / 2 * 0.8, animatedPieceWidth! / 2 * 0.8,
paint, paint,
); );
} }

View File

@ -27,9 +27,14 @@ class Board extends StatelessWidget {
final double width; final double width;
final double height; final double height;
final Function(BuildContext, int) onBoardTap; final Function(BuildContext, int) onBoardTap;
final animationValue;
final String tag = "[board]"; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -49,6 +54,7 @@ class Board extends StatelessWidget {
position: Game.instance.position, position: Game.instance.position,
focusIndex: Game.instance.focusIndex, focusIndex: Game.instance.focusIndex,
blurIndex: Game.instance.blurIndex, blurIndex: Game.instance.blurIndex,
animationValue: animationValue,
), ),
child: container, child: container,
); );

View File

@ -55,7 +55,8 @@ class GamePage extends StatefulWidget {
_GamePageState createState() => _GamePageState(); _GamePageState createState() => _GamePageState();
} }
class _GamePageState extends State<GamePage> with RouteAware { class _GamePageState extends State<GamePage>
with RouteAware, SingleTickerProviderStateMixin {
String? _tip = ''; String? _tip = '';
bool isReady = false; bool isReady = false;
bool isGoingToHistory = false; bool isGoingToHistory = false;
@ -64,6 +65,8 @@ class _GamePageState extends State<GamePage> with RouteAware {
pixelRatio: 1.0, pixelRatio: 1.0,
skipFramesBetweenCaptures: 0, skipFramesBetweenCaptures: 0,
); );
late AnimationController _animationController;
late Animation animation;
final String tag = "[game_page]"; final String tag = "[game_page]";
@override @override
@ -79,6 +82,8 @@ class _GamePageState extends State<GamePage> with RouteAware {
timer = Timer.periodic(Duration(microseconds: 100), (Timer t) { timer = Timer.periodic(Duration(microseconds: 100), (Timer t) {
_setReadyState(); _setReadyState();
}); });
_initAnimation();
} }
_setReadyState() async { _setReadyState() async {
@ -95,6 +100,32 @@ class _GamePageState extends State<GamePage> 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) { showTip(String? tip) {
if (!mounted) return; if (!mounted) return;
if (tip != null) print("[tip] $tip"); if (tip != null) print("[tip] $tip");
@ -142,6 +173,10 @@ class _GamePageState extends State<GamePage> with RouteAware {
return false; return false;
} }
_animationController.duration =
Duration(milliseconds: (Config.animationDuration * 1000).toInt());
_animationController.reset();
if (Game.instance.engineType == EngineType.aiVsAi || if (Game.instance.engineType == EngineType.aiVsAi ||
Game.instance.engineType == EngineType.testViaLAN) { Game.instance.engineType == EngineType.testViaLAN) {
print("$tag Engine type is no human, ignore tapping."); print("$tag Engine type is no human, ignore tapping.");
@ -448,6 +483,9 @@ class _GamePageState extends State<GamePage> with RouteAware {
Move mv = response.value; Move mv = response.value;
final Move move = new Move(mv.move); final Move move = new Move(mv.move);
_animationController.duration =
Duration(milliseconds: (Config.animationDuration * 1000).toInt());
_animationController.reset();
Game.instance.doMove(move.move); Game.instance.doMove(move.move);
showTips(); showTips();
break; break;
@ -1111,6 +1149,7 @@ class _GamePageState extends State<GamePage> with RouteAware {
child: Board( child: Board(
width: boardWidth, width: boardWidth,
onBoardTap: onBoardTap, onBoardTap: onBoardTap,
animationValue: animation.value,
), ),
); );
} }
@ -1291,8 +1330,9 @@ class _GamePageState extends State<GamePage> with RouteAware {
@override @override
void dispose() { void dispose() {
print("$tag dipose"); print("$tag dispose");
widget.engine.shutdown(); widget.engine.shutdown();
_animationController.dispose();
super.dispose(); super.dispose();
routeObserver.unsubscribe(this); routeObserver.unsubscribe(this);
} }