寫在前面
適用于對flutter動畫有一定了解的同學,本案例是為了模擬一個角色對象根據(jù)不同狀態(tài)變換不同效果
先看下效果圖
0jjg5-ur642.gif
實現(xiàn)&代碼
定義狀態(tài)&動畫
enum CellStatus { none, mot, pen, def }
- non: none/常規(guī)狀態(tài)皆撩,無動畫
- mot: motion/行動狀態(tài)扣墩,背景閃爍
- pen: pending/待選狀態(tài),抖動
- def: defense/防守狀態(tài)扛吞,尖角變圓角
定義角色對象
class Cell {
CellStatus status;
Notify1<CellStatus> onStatusChanged;
}
本例中只包含兩個屬性
- status: CellStatus呻惕,表示當前狀態(tài)
- onStatusChanged: Notify1<CellStatus>,表示狀態(tài)變化的回調
這里的Notify1是自定義的一個帶一個泛型參數(shù)的方法對象
當需要變化狀態(tài)時滥比,比如轉換到行動狀態(tài)
cell.onStatusChanged?.call(CellStatus.mot)
其實更常見的辦法是讓Cell繼承于ChangeNotifier亚脆,然后通過addListener來添加監(jiān)聽變化,相比于onStatusChanged: Notify1<CellStatus>盲泛,后者只針對單一屬性濒持,具有指向性。
頁面布局
上方是4個按鈕寺滚,模擬狀態(tài)變化柑营,下方是展示角色對象的CellWidget
class CellApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// 這里創(chuàng)建角色對象
Cell cell = Cell();
var children = [
Spacer(),
RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.none), child: Text("None")),
Spacer(),
RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.mot), child: Text("Mot")),
Spacer(),
RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.pen), child: Text("Pen")),
Spacer(),
RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.def), child: Text("Def")),
Spacer(),
];
return Scaffold(
body: SafeArea(
child: Column(children: [
Expanded(child: Center(child: Row(children: children, mainAxisSize: MainAxisSize.min))),
Expanded(child: Container(alignment: Alignment.topCenter, child: CellWidget(cell)))
]),
));
}
}
CellWidget
這是一個需要應用動畫的widget,傳入Cell對象玛迄。其State中加入AnimationController
class CellWidget extends StatefulWidget {
final Cell cell;
CellWidget(this.cell);
@override
_CellWidgetState createState() => _CellWidgetState();
}
class _CellWidgetState extends State<CellWidget> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
_controller.addListener(() => setState(() {}));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
...
}
監(jiān)聽狀態(tài)變化
就是為cell.onStatusChanged賦值
@override
void initState() {
super.initState();
...
widget.cell.onStatusChanged = onStatusChanged;
}
void onStatusChanged(CellStatus newStatus) {
...
}
處理狀態(tài)變化
其實就是創(chuàng)建根據(jù)狀態(tài)創(chuàng)建Animation
- 當為motion時由境,創(chuàng)建背景閃爍動畫,使用ColorTween創(chuàng)建開始和結束的顏色蓖议,并調用controller.repeat(reverse: true)來實現(xiàn)重復
- 當為pending時虏杰,抖動動畫是通過四向隨機改變widget位置來實現(xiàn)的,所以只需在每次重繪時通過Random#nextInt()來隨機方向即可勒虾,此處不用創(chuàng)建任何動畫纺阔,只需讓controller重復即可
- 當為defense時,尖角變圓角修然,使用Tween創(chuàng)建開始和結束時圓角的半徑即可笛钝,不需重復
- 而none狀態(tài),需要回歸正常愕宋,由于正常值是預定義的玻靡,所以此處僅僅需要關閉動畫
void onStatusChanged(CellStatus newStatus) {
print("cell status change from ${widget.cell.status.toString()} to ${newStatus.toString()}");
widget.cell.status = newStatus;
setState(() {});
_controller.reset();
_controller.duration = Duration(milliseconds: 200);
if (widget.cell.status == CellStatus.mot) {
_animation = ColorTween(begin: Colors.white, end: Colors.red)
.animate(CurvedAnimation(parent: _controller, curve: Curves.easeInCubic));
_controller.repeat(reverse: true);
} else if (widget.cell.status == CellStatus.none) {
_controller.stop();
} else if (widget.cell.status == CellStatus.pen) {
_controller.repeat(reverse: true);
} else if (widget.cell.status == CellStatus.def) {
_animation = Tween(begin: 0.0, end: 16.0).animate(_controller);
_controller.duration = Duration(milliseconds: 800);
_controller.forward();
}
}
build不同狀態(tài)下的widget
正常情況下,應該是一個Container中贝,寬高為64.0囤捻,居中一個Text,decoration為白底邻寿,邊框為尖角
由于抖動動畫需要改變位置蝎土,所以外層應當加Transform.translate(offset, widget)
@override
Widget build(BuildContext context) {
var color = Colors.white;
var offset = Offset.zero;
var radius = 0.0;
if (widget.cell.status == CellStatus.mot) {
color = _animation.value;
} else if (widget.cell.status == CellStatus.pen) {
var random = new Random().nextInt(4);
/// left:0 right:1 top:2 bottom:3
double x = 0, y = 0;
switch (random) {
case 0: x = -2; break;
case 1: x = 2; break;
case 2: y = -2; break;
case 3: y = 2; break;
}
offset = offset.translate(x, y);
} else if (widget.cell.status == CellStatus.def) {
radius = _animation.value;
}
var decoration = BoxDecoration(color: color, border: Border.all(color: Colors.blue), borderRadius: BorderRadius.circular(radius));
var container = Container(width: 64.0, height: 64.0, decoration: decoration, alignment: Alignment.center, child: Text("Cell"));
return Transform.translate(offset: offset, child: container);
}
最后
本人熱愛編程视哑,熱愛游戲,熱愛flutter誊涯,一直想用flutter寫一個RPG類型的游戲挡毅,有喜歡flutter開發(fā)游戲的朋友可以加我好友互相交流哈!