Qiita株式会社 Advent Calendar 2021(2)の12日目の担当は、CX向上グループの@xrxoxcxoxです!
この記事の概要
最近、LGTMボタンにアニメーションをつけました。
こちらをどのように実装していったのかを簡単に説明します。
全体の流れ
- After Effectsでアニメーションを作り、Bodymovinでjson書き出し
- Lottie(react-lottie)でReact上で動かす
- 実際に触ってみて調整
After Effectsでアニメーションを作り、Bodymovinでjson書き出し
もととなるデータを作っていきましょう。
最終出力がsvgアニメーションになるのもあって、After Effectsの全ての機能が使えるわけではありません。
以下のことに気をつけながらアニメーションを考えます。
- シェイプレイヤーかパスデータだけで作る
- 複雑なマスクを使わない
- 基本、エフェクトは使えないものと考える
一旦仕上がったものはこんな感じ。
Bodymovinのインストール方法や詳しい使い方は省いてしまいましたが、@naomunaomuさんの以下の記事が分かりやすいのでおすすめです。
Lottie(react-lottie)でReact上で動かす
コード上で動かすにあたり、公式から出ているライブラリはlottie-web
です。
QiitaのフロントはReactなのですが、上記のライブラリはReactには最適化されていません。
そのため、react-lottieを代わりに使いました。
react-lottieは更新がしばらくないのが気になりますが、今回はスター数の多さで選びました。
もしご自分でも試してみようと思った場合は、他のライブラリも含めて検討してみてください。
ハマった点
単に動かすだけであればLottie
コンポーネントにアニメーションデータのjsonを渡すだけで良いのですが、Qiitaの場合はページに訪れた際の状態で分岐が発生します。
- まだLGTMしていない記事の場合
- ボタンは未LGTM状態(0フレーム目)
- クリックするとアニメーションしてLGTM済みの状態(最終フレーム)に遷移する
- もう1度クリックすると未LGTM状態(0フレーム目)に戻る
- 2と3の繰り返し
- 既にLGTMしている記事の場合
- ボタンはLGTM済みの状態(最終フレーム)
- クリックすると未LGTM状態(0フレーム目)に戻る
- もう1度クリックするとアニメーションしてLGTM済みの状態(最終フレーム)に遷移する
- 2と3の繰り返し
何を当たり前のことを言っているんだと思われるかもしれません。
しかし、Lottieには「任意のフレームからスタートする」機能がないのです。
そのため2の「既にLGTMしている記事」に訪れた場合に「LGTM済みの状態(最終フレーム)」で提供できません。
色々工夫してみたものの、ページに訪れる度にアニメーションしてしまうとか、変な動きになってしまいました。
ちなみにだいぶ古いですがこのようなIssueも立っていて、ハック的な方法が示されています笑1
解決した方法
まずは以下の2つの要素を用意しました。
- LGTM済みの見た目の静的なsvg
- アニメーションするsvg(= Lottie)
そして2つを出し分けます。
実際のコードだと色々な要素が含まれているので、簡略化して以下に載せます。
interface Props {
isLiked: boolean
}
const LgtmButton = ({ isLiked }: Props) => {
const [isAnimationWorked, setIsAnimationWorked] = useState(false)
return (
<button onClick={() => {setIsAnimationWorked(true)}>
{!isAnimationWorked && isLiked ? (
<LgtmCompleted />
) : (
<Lottie
isStopped={!isLiked}
// その他、オプションの指定色々
/>
)}
</button>
)
}
-
isLiked
はグローバルに管理、isAnimationWorked
はローカルに管理 - ページに訪れた時点では
isAnimationWorked
は絶対にfalseなため、LGTM済みかどうかで分岐する- LGTM済みであれば
<LgtmCompleted />
(LGTM済みの見た目の静的なsvg)が表示される - 未LGTMであれば再生前の
<Lottie />
が表示される
- LGTM済みであれば
- ボタンをクリックした時点で
isAnimationWorked
がtrueになる- LGTM済みでボタンをクリックした場合は
isLiked
がfalseになるため再生前の<Lottie />
に切り替わる - 未LGTMでボタンをクリックした場合は
isLiked
がtrueになるためLottieのisStopped
がfalseとなり、動き出す
- LGTM済みでボタンをクリックした場合は
かなり苦労しましたが、こういった方法で実現できました。
実際に触ってみて調整
さきほどAfter Effectsで作成したときと、実際に押せるボタンとして実装したときとではアニメーションの印象が若干違いました。
そのため、一度実装した後にタイミングやデュレーションなど少しずつ調整……。
数フレームずつ速くしたり遅くしたりを繰り返してみつつ、なんとか完成にこぎつけました。
まとめ
- After Effectsで作ったアニメーションを簡単にjsonに書き出して使えるけど、エフェクトをつけるなど凝ったことはできないので注意
- Lottieは最終フレームでフリーズさせる機能がないので条件分岐で実装
- 最後は目で見て、実際に触って確認。
-
更にこの方法は
lottie-web
ならできそうなもののreact-lottie
ではできないっぽかったです……。 ↩