こんにちは。京都開発部の柴坂浩行です。フロントエンドエンジニアとして、LINE MUSIC の Web アプリ開発を担当しています。
毎年 LINE MUSIC では、みなさんが1年間に聴いた曲を振り返ることができる企画を実施しています。昨年末にも振り返り特設サイト「#LINEMUSICで振り返る2023」を公開しました。
https://music.line.me/webapp/lmplayback2023(公開終了)
この記事では、振り返り特設サイトで作ったアニメーションの実装のコツについてご紹介します。
アニメーションの実装に至るまで
まず、ウェブサイトのアニメーションはどのように決まっていく のでしょうか?
プランナーが思い浮かべるイメージ、デザイナーの考える見栄え、エンジニアの考える実装。最終的には、各々が期待する形を一致させなければなりません。
今回は、以下のような流れでした。
- ウェブサイトデザインの初稿が完成
- デザインミーティング(※1)でアニメーションの方針を相談
- 方針をもとにデザインファイル上にデザイナーが動きの指示を記載
- エンジニアが動きの大きさ、速さ、タイミングなどを具体化し、アニメーションデモ(※2)を実装
- デザインミーティングや Slack 上で相談しながら調整
アニメーションデモでは動きに関わる部分のみ(※2)コーディングして、実際にブラウザで見たときの印象をデザイナーとプランナーに確認してもらいました。
動きの大きさ、速さ、タイミングなどは動かしてみないと善し悪しがわからないので、エンジニアが実装時に初期案として考えました。そこから足したり引いたりの調整を繰り返して最終的な動きを決めていきました。
※1 プランナー、デザイナー、エンジニアが集まってUIの検討を行う会議
※2 動きに関わらない部分はデザインファイルから書き出したUIの画像をそのまま貼り付けて実装し、早さ優先で特定の画面幅のみで見られるデモにしました
実装した主なアニメーション
ここからは、サイトに実装した中で特に工夫したアニメーションをいくつか紹介します。
- フェードインアニメーション
- SVG明滅アニメーション
- プログレスバーアニメーション
- カウントアップアニメーション
- 紙吹雪アニメーション
- ボタンの省略表示アニメーション
アニメーションを過剰にするとユーザー体験が損なわれることもあるので、これらのアニメーションの特性を理解してバランスを取ることを重視しました。
例えば、フェードインアニメーションはページの全域にわたって使用していますが、ユーザーアクションを期待する場所では目が奪われないよう使用しないことにしました。一方で特徴を出したいところでは、プログレスバーやカウントアップ、紙吹雪などでメリハリをつけています。こういったバランス調整を、レビューで意見を出し合って行いました。
それでは、それぞれの解説に移ります。
このプロジェクトでは Vue.js (Options API) や SASS を使用しており、それぞれのコードも一部ご紹介します。
フェードインアニメーション
多くのコンポーネントに適用した汎用的なアニメーションです。ユーザーアクションを求めるところには適用しないなど、過剰に使用しないよう気を配りました。
アニメーションとしては、印象的な動きにするための立体的な動きがポイントです。
デザイナーからは、透明度をつけるだけでなく下からゆっくり上がるようにしてほしいという要望がありました。実装時にはそれに加えてアニメーション前のスケールを少しだけ小さくすることでより立 体感をつけています。大きさの変化に気づく人は少ないと思いますが、隠し味のような小技です。
実装イメージ
.fadein_section {
.fadein {
opacity: 0;
transform: translate(0, 20px) scale(0.99); // わずかに小さくしておく
transition-property: opacity, transform;
transition-duration: 1s;
transition-delay: 0;
}
&.active .fadein {
opacity: 1;
transform: translate(0, 0) scale(1); // アニメーション後に等倍に
}
}
SVG明滅アニメーション
背景に多用した、光が明滅するアニメーションには、SVG と CSS Animation を組み合わせました。
SVG内の複数の光のパーツを、CSS Animation を用いてそれぞれ異なるタイミングで明滅させています。
異なるタイミングで明滅させるためには、それぞれを別の画像として配置してアニメーションを設定する必要がありますが、数が多くなるほど実装コストが高くなります。そこで、複数の光のパーツをまとめて1個のSVG画像として用意し、その中でタイミングをずらしたアニメーションを光のパーツごとにかけることにしました。
SVGの内部にCSSが効くことはよく知られていますが、今回は透明度を変化させるだけなので問題なく動作します。デザインデータから書き出した SVG の内部に、クラス名を追加する程度の変更で済むため実装は非常に簡単でした。また Vue.js を使っているため SVG と CSS を1つの Component として扱い、管理しやすくしています。
実装イメージ
元SVG
<svg>
<g>
<path></path>
<path></path>
<path></path>
<path></path>
<path></path>
</g>
</svg>
Vue Component 化
<template>
<svg>
<g class="twinkle">
<path></path>
<path></path>
</g>
<g class="twinkle">
<path></path>
<path></path>
</g>
<g class="twinkle">
<path></path>
</g>
</svg>
</template>
<style lang="scss" scoped>
.twinkle {
opacity: 0;
animation: twinkle 4s ease infinite;
$animation-delay: 1.5s;
@for $i from 1 through 10 {
&:nth-child(#{$i}) {
animation-delay: #{$animation-delay * ($i - 1)};
}
}
}
</style>
この手法では、SVG画像全体が都度再描画されるためCPU負荷が高くなる可能性があります。そのためパフォーマンス測定を行うなど、負荷対策には配慮して実装されることをおすすめします。
プログレスバーアニメーション
スクロールに応じて、真っすぐ引いたバーの上に、緑のバーが伸びていきます。また見出しの横では線上にドットが配置され、緑のバーにあわせて色が付きます。