🌊
【Flutter】夏が終わる前におれと波に乗らないか?【アニメーション】
「みんな、おれと波に乗らねえか?」
ということで、今回作成したアニメーションがこちらです。
可愛くないですか?!!!
Flutterでアニメーションってどうやるんだろう、と思って勉強してみたので簡単にご紹介したいと思います!
アニメーションに関しては色々な記事があってそちらの方が参考になるので記事を貼っておきます! こんなことができるんだという周知のために記事を書いたので細かい実装についてはあまり触れないです!!
実装についてはGitHubにコードを上げておくので読んでみてください!
main.dartにコピペすれば動くよ!
実装
アニメーションの基礎としてサーファーが上下する部分のみ解説します。波の実装に関しては下記の記事を参考にして作成しました! CustomClipper
を用いて波を表現していて非常に面白いです!
サーファーの実装
サーファーをどうやって上下させてるねんって話ですが簡単です
-
AnimationController
を準備(0~1の値を無限にリピートできる) -
AnimationController
の値を元に三角関数で高さを出す - サーファーの画像を用意
-
Positioned
でサーファーの高さを変える
AnimationControllerを使用する準備
そのクラスで利用するAnimationController
が1つだけの場合はSingleTickerProviderStateMixin
をmixinしたStateを作成する必要があります。
↓アニメーションについて詳しく知りたい方向け
class WavePage extends StatefulWidget {
const WavePage({Key? key}) : super(key: key);
_WaveState createState() => _WaveState();
}
/// vsync:毎フレームごとに更新を伝えるものmixin
/// SingleTickerProviderStateMixinを適用する必要がある
class _WaveState extends State<WavePage> with SingleTickerProviderStateMixin {
// アニメーションを制御するコントローラー
late AnimationController _animationController;
void initState() {
super.initState();
//初期化
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1000),//1秒で0->1に到達
);
_animationController.repeat(); //0->1をリピートする
}
void dispose() {
_animationController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
// AnimatedBuilderでラップし_animationControllerを設定
body: AnimatedBuilder(
animation: _animationController,
builder: (context, child) => ...任意のWidget()
),
);
}
}
三角関数で高さを出す
いわゆるサインコサインタンジェントのsin関数を使って上下運動を表します
double f(double b) {
final height = MediaQuery.of(context).size.height; // 画面の高さ
// 係数は適当に設定
final y = math.sin(b * 2 * math.pi) * 30 + height * 0.4;
return y;
}
サーファーの画像を用意
サーファークラスを作成します
class Surfer extends StatelessWidget {
const Surfer({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return SizedBox(
width: 10,
child: Image.asset(
'images/surfing_woman.png',
),
);
}
}
Positionedで高さを変化させる
0~1を周期的に変化し続ける_animationController.value
を先ほどの三角関数にぶちこんで高さを変化させる
Positioned(
left: MediaQuery.of(context).size.width / 2,
top: f(_animationController.value), // 高さが sin関数に従って変化する
width: 100,
child: const Surfer(),
),
コピペで動くサーファー
main.dart
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) =>
MaterialApp(theme: ThemeData(), home: const WavePage());
}
class WavePage extends StatefulWidget {
const WavePage({Key? key}) : super(key: key);
// ignore: library_private_types_in_public_api
_WaveState createState() => _WaveState();
}
/// vsync:毎フレームごとに更新を伝えるものmixin
/// SingleTickerProviderStateMixinを適用すれば良い
class _WaveState extends State<WavePage> with SingleTickerProviderStateMixin {
// アニメーションを制御する
late AnimationController _animationController;
void initState() {
super.initState();
//初期化
_animationController = AnimationController(
vsync: this, //お決まり
duration: const Duration(milliseconds: 1000),
);
_animationController.repeat(); //リピート設定
}
void dispose() {
_animationController.dispose();
super.dispose();
}
// サーファーの動きを制御する関数 いわゆる三角関数で上下させる
double f(double b) {
final height = MediaQuery.of(context).size.height; // 画面の高さ
final y = math.sin(b * 2 * math.pi) * 30 + height * 0.4;
return y;
}
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBuilder(
animation: _animationController,
builder: (context, child) => Stack(
children: <Widget>[
// サーファーのアニメーション
Positioned(
left: MediaQuery.of(context).size.width / 2,
top: f(_animationController.value), // 高さが sin関数に従って変化する
width: 100,
child: const Surfer(),
),
],
),
),
);
}
}
class Surfer extends StatelessWidget {
const Surfer({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return SizedBox(
width: 10,
child: Image.asset(
'images/surfing_woman.png',
),
);
}
}
波を表示させたい場合はStack
で重ねて良い感じに表現しましょう!
さいごに
Flutterでもこういうことができるんだ、ということが知ってもらえたら幸いです! 最後まで読んでいただきありがようございました!!
Discussion