こんにちは、 id:numanuma08 です。5月になって天気の良い日も多くて山が捗りますね。Airbnbの作っているアニメーションフレームワークLottieを使ってユーザーがボタンを押したときにアニメーションを開始、アニメーションが終わったらコールバックを呼び出す処理を実現したくなったので、そのサンプルコートです。公式ドキュメントを読んでもいまいち掴みどころがなくてちょっと調べることになりました。
iOS(SwiftUI)、Android(Jetpack Compose)を対象としています。
実現したいこと
- ユーザーがボタンを押すとアニメーションが再生される
- アニメーションが終了したらコールバックメソッドが呼び出される
リソースの準備
Lottieはアニメーショをいくつかのファイル形式で定義できますが、今回はlottie形式のバイナリを使いました。各プラットフォームごとのファイルの取り扱い方法を説明します。
iOS
Assets Bundleにファイルを格納します。Assets Bundleに適当な名前をつけてアニメーション定義ファイルを登録します。もし必要なら、デバイスやテーマ別などでアニメーション定義ファイルを変更できます。
Android
res/raw
ディレクトリにファイルを格納します。こちらも必要ならデバイスの画面サイズやテーマ、言語などでアニメーションを変更できます。
アニメーション用のコンポーネントをレイアウトする
画面の必要な場所にアニメーション用のコンポーネントを配置します。このときリソースの読み込みを行います。
iOS
LottieView
を使います。DotLottieFile.asset
を使ってアニメーション定義ファイルを読み込んでアニメーションを再生します。
LottieView { try await DotLottieFile.asset(named: "CheckmarkAnimation") }
Android
LottieAnimation
を使います。rememberLottieComposition
でアニメーション定義ファイルを読み込み、Floatを返すラムダ式をパラメータに渡します。
val checkmarkAnimation by rememberLottieComposition(spec = LottieCompositionSpec.RawRes(R.raw.checkmark2)) LottieAnimation( composition = checkmarkAnimation, progress = { 0f} )
アニメーションを制御する
「ボタンを押したらアニメーションを1度だけ再生する」を実現するため、アニメーションの制御をします。
iOS
LottiePlaybackMode
をstateで定義しておいてボタンを押したときに値を変化させます。
@State var checkmarkAnimationPlaybackMode: LottiePlaybackMode = .paused(at: .progress(0)) Button( action: { // アニメーションを最初から最後まで1度だけ再生する checkmarkAnimationPlaybackMode = .playing(.fromProgress(0.0, toProgress: 1, loopMode: .playOnce)) }, label: { Text("I am a button") } ) LottieView { try await DotLottieFile.asset(named: "CheckmarkAnimation") } .playbackMode(checkmarkAnimationPlaybackMode)
Android
LottieAnimatable
を生成してLaunchedEffect
などで状態の変更をトリガーに値を変更し、アニメーションを行います。
var isChecked by remember { mutableStateOf(false) } val checkmarkAnimation by rememberLottieComposition(spec = LottieCompositionSpec.RawRes(R.raw.checkmark2)) val checkmarkAnimatable = rememberLottieAnimatable() LaunchedEffect(isChecked) { if (isChecked) { // animateはsuspend関数。アニメーションを最初から最後まで1度だけ再生する checkmarkAnimatable.animate( checkmarkAnimation, clipSpec = LottieClipSpec.Progress() ) } } Button( modifier = Modifier.fillMaxWidth(), onClick = { isChecked = true } ) { Text(text = "I am a button") } LottieAnimation( composition = checkmarkAnimation, // animatableのprogressを使って再生位置の描画をする progress = { checkmarkAnimatable.progress }, )
アニメーションの終了を検知する
アニメーションの終わりをトリガーに処理をしたいので、終了を検知しなければなりません。それぞれのプラットフォームの方法で行います。
iOS
animationDidFinish
モディファイアを使います。このコールバックが呼ばれたらアニメーションの終了です。
LottieView { try await DotLottieFile.asset(named: "CheckmarkAnimation") } .playbackMode(checkmarkAnimationPlaybackMode) .animationDidFinish { _ in // アニメーション終了時の処理を行う }
Android
LottieAnimatable.animate
はsuspend関数なので、関数の実行が終わったらアニメーションが終わった扱いとなります。
LaunchedEffect(isChecked) { if (isChecked) { // animateはsuspend関数。アニメーションを最初から最後まで1度だけ再生する checkmarkAnimatable.animate( checkmarkAnimation, clipSpec = LottieClipSpec.Progress() ) // アニメーション終了時の処理を行う } }
まとめ
Lottie Animationを使ってユーザーの操作をトリガーにアニメーションを開始し、終了を検出するiOSとAndroidの方法をまとめました。公式リファレンスを見ても説明が断片的で、ソースコードやサンプルコードから情報収集が必要だったので忘備録的な記録です。同じことをやろうと思っている人の役に立ったら幸いです。
各種公式リファレンスへのリンクはコチラ
Android(Jetpack Compose) airbnb.io
iOS(SwiftUI, UIKit)
あと、はてなブログに動画を載せるのが地味に面倒でした。Google Driveに配置したファイルの埋め込み用共有リンクを使っています。