flutter: Support piece animation setting
This commit is contained in:
parent
f32e9eb20c
commit
c0225d63ad
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue