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';
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 = <PiecePaintPair>[];
final piecesToDraw = <PiecePaintParam>[];
// 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,
);
}

View File

@ -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,
);

View File

@ -55,7 +55,8 @@ class GamePage extends StatefulWidget {
_GamePageState createState() => _GamePageState();
}
class _GamePageState extends State<GamePage> with RouteAware {
class _GamePageState extends State<GamePage>
with RouteAware, SingleTickerProviderStateMixin {
String? _tip = '';
bool isReady = false;
bool isGoingToHistory = false;
@ -64,6 +65,8 @@ class _GamePageState extends State<GamePage> 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<GamePage> with RouteAware {
timer = Timer.periodic(Duration(microseconds: 100), (Timer t) {
_setReadyState();
});
_initAnimation();
}
_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) {
if (!mounted) return;
if (tip != null) print("[tip] $tip");
@ -142,6 +173,10 @@ class _GamePageState extends State<GamePage> 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<GamePage> 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<GamePage> with RouteAware {
child: Board(
width: boardWidth,
onBoardTap: onBoardTap,
animationValue: animation.value,
),
);
}
@ -1291,8 +1330,9 @@ class _GamePageState extends State<GamePage> with RouteAware {
@override
void dispose() {
print("$tag dipose");
print("$tag dispose");
widget.engine.shutdown();
_animationController.dispose();
super.dispose();
routeObserver.unsubscribe(this);
}