Teratailに投稿した内容そのままなのですが、普通に有用そうなので記事としてまとめておきます。
はじめに
ログイン画面などで、背景が延々と流れているようなアプリを一度は見たことがあると思います。
(僕はないかもしれません)
このような要望は、割とアプリ開発ではあるあるかなーと思ったのですが、
調べてもあまり出てこなかったので、いろいろ調べて実装してみました。
コード全文
今回は先にコードを提示します。
AnimationController
を使うので、StatefulWidget
の想定です。
また、一部抜粋なのでよしなにお願いします。
// デバイスの画面サイズ(高さ)
late double deviceHeight;
// 画像の横幅
late double imageWidth;
// アニメーションコントローラー
late AnimationController controller;
// アニメーション
late Animation<RelativeRect> rectAnimation;
// contextを受け取るために、didChangeDependenciesで初期化する
@override
void didChangeDependencies() {
super.didChangeDependencies();
// デバイスの画面サイズ(高さ)取得
deviceHeight = MediaQuery.of(context).size.height;
// 画像の縦横比
double aspectRatio = 1.8;
// 画像の横幅計算
imageWidth = deviceHeight * aspectRatio;
// コントローラー初期化
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 10),
)..repeat();
// 1枚目が終わったら止めてリピートする
rectAnimation = RelativeRectTween(
begin: RelativeRect.fromLTRB(imageWidth, 0, 0, 0),
end: RelativeRect.fromLTRB(0, 0, imageWidth, 0),
).animate(
CurvedAnimation(parent: controller, curve: Curves.linear),
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: PositionedTransition(
rect: rectAnimation,
child: OverflowBox(
maxWidth: imageWidth * 2, // 画像2枚分
maxHeight: deviceHeight, // 画面の高さに合わせる
child: Row(
children: <Widget>[
Image(
height: deviceHeight, // 画面の高さに合わせる
fit: BoxFit.fitHeight,
image: const AssetImage('画像のパス'),
),
Image(
height: deviceHeight, // 画面の高さに合わせる
fit: BoxFit.fitHeight,
image: const AssetImage('画像のパス'),
),
],
),
),
),
);
}
アプリで使うなら、Scaffold
のbodyにStack
を置いて、
アニメーションする背景の上にログインボタンを置いたりして使います。
コメント書いてるので不要かも知れませんが、一応解説しておきます。
画面サイズ、画像サイズ取得
今回は、画面を覆うサイズの画像を使うことを想定しています。
縦幅は画面より大きくなるようにし、横にスクロールさせるので、横長の長方形になるように
画像を作ったり、作ってもらいます。
(タブレットとか考えたら縦が最低1366px以上?実解像度を考えたらかなり大きめに作った方がいいです)
実際にアプリに表示させる画像は、縦幅は画面サイズに合わせます。
横幅はいろんな場所で指定するので、画像の縦横比を掛けて計算しておきます。
アニメーションの設定
今回のキモはrectAnimation
です。
ちょうど画像1枚分アニメーションさせて、リピートで繰り返します。
2枚目の画像が画面の左端に来たときに最初の状態に戻ることで、切れ目のないループを作り出せます。
(逆に言えば、2枚目の画像は1枚目と同じものではなく、画面に映る範囲でいいのですが、手間なので同じ画像を使用します)
OverflowBoxで画面サイズを超える
普通にRow
で画像を並べようとしても、うまくいきません。
ですので、OverflowBox
を使って親より大きくなることを許可します。
縦は画面サイズの縦幅、横は画像の横幅*2で画像2枚分ピッタリになります。