これは何?
[CSSアニメーション]三(卍^o^)卍←こいつのこれ→卍を回したい
- mame_hashbill氏の記事を読み,「ぼくもこれやりたい!」ってなったため
- Flutterのアニメーション復習したかったため
- DartPadでFlutterが動かせるようになっていたのでリンクで共有するやつをやりたかったため
以上の理由でやりました.
怒られたら消します.
まずはこれを見てくれ
スマホでも動くけど,横画面にするかPCで見たほうが良いです.
右上のRunボタンを押すと,アイツが動き出すはずです.
実装
Flutterのアニメーションにはいくつか実装の方法があるんですが,今回は細かくアニメーションを制御できる最も基本的な書き方のうち2種類を使います.
2つとも同じような方法ですが,実際に書いてみて比べたくなったのでやってみました.
普通にAnimationを直に使う場合
公式チュートリアル
これの通り.ドゥルルル自体の移動に使ってます.
@override
void initState() {
super.initState();
//省略
_bodyController = AnimationController(
duration: const Duration(milliseconds: 2500),
vsync: this,
);
_bodyAnimation = Tween<double>(begin: 0, end: 600).animate(_bodyController)
..addListener(() => setState((){}));
_bodyController.repeat();
}
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(_bodyAnimation.value, 0),
child: _buildCrazy(context),
);
}
AnimatedBuilder
を使う場合
公式ドキュメント
これの通りです.腕の回転ですね.
@override
void initState() {
super.initState();
_armController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
)..repeat();
//省略
}
Widget _buildCrazy(BuildContext context) {
return AnimatedBuilder(
animation: _armController,
builder: (context, child) {
return Row(
children: <Widget>[
const Text('三('),
Transform.rotate(
angle: _armController.value * 2.0 * math.pi,
origin: Offset.zero,
child: Text('卍'),
),
const Text('^o^)'),
Transform.rotate(
angle: _armController.value * 2.0 * math.pi,
origin: Offset.zero,
child: Text('卍'),
),
const Text('ドゥルルルルルルルル'),
],
);
},
);
}
(アニメーションタイプが回転でrepeatメソッド使うのもまんまだったのでびっくり)
2つの実装,どっち使えばいいの?
ここでは簡単な実装だったので,どっちでもいいじゃんって感じになってるんですけど,一応使い分けポイントとしては
-
AnimatedBuilder
の場合,animation
にコントローラーを直接突っ込める- つまり
Animation
の宣言をしなくてもアニメーションできて手間が減る
- つまり
(箇条書きしたけど一つしか出なかった)
というのがパッと見わかります.
罠
しかし上記の使い分けには罠があります.
確かに AnimatedBuilder
にコントローラーを突っ込めば楽ができますが,この場合アニメーションの種類は0.0 ~ 1.0のTweenアニメーションで固定になります.またイージングカーブもLinear固定になります.
でも, AnimatedBuilder
のプロパティ名って animation
ですよね?
そう,普通に Animation
が突っ込めます.
AnimationController _armController;
Animation<double> _armAnimation;
@override
void initState() {
super.initState();
_armController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
)..repeat();
_armAnimation = Tween<double>(begin: 0, end: 2.0 * math.pi).animate(_armController);
}
@override
Widget build(BuildContext context) {
return _buildCrazy(context);
}
Widget _buildCrazy(BuildContext context) {
return AnimatedBuilder(
animation: _armAnimation,
builder: (context, child) {
return Row(
children: <Widget>[
const Text('三('),
Transform.rotate(
angle: _armAnimation.value,
origin: Offset.zero,
child: Text('卍'),
),
const Text('^o^)'),
Transform.rotate(
angle: _armAnimation.value,
origin: Offset.zero,
child: Text('卍'),
),
const Text('ドゥルルルルルルルル'),
],
);
},
);
}
腕のアニメーションだけ抜き出して,書き換えてみました.
これで細かいアニメーションの設定もできるし,更にリスナーを登録する必要がないので無駄にsetState書かなくてスッキリですね😋
結局の所
Animatedなんちゃら系の一番ラクなWidgetを使わずにアニメーションを実装する場合,基本的にAnimatedBuilderで組むのがよさそうです.
公式チュートリアル の Monitoring the progress of the animation でやっているような場合分けも addStatusListener
するだけで済みます.
結果的に書く量ってそこまで変わらない気がするので,どちらの方法でやってもあまり変わらない……? もし明確な違いがあったらコメントで教えて下さい.