マジで時間が取れなかったので簡単な実装の話
はじめに
某鯖で開催されたアドベントカレンダー企画、20日目の記事です。
記事として新規でサイトを作って、その実装について話すつもりだったけど時間取れなかったよ……。
そもそもガッツリした実装の話しても大抵の人にはあまり役に立たないので今回は簡単なアニメーションの考え方や実装について書くことにしました。
記事中のコードやサンプルは時間がないことからかなり適当に書いているのでクオリティは高くないと思います。
ただ、0から知る人に考え方の幅を与えることができれば目標としては十分なのでなんとなくの参考にしてください。
(書いていてTAやってたときのこと思い出しますね)
アニメーションの実装?
アニメーションというと皆さんはどんなものを想像するでしょうか?
秒間24枚で手描きを……など想像した方もいるかも知れませんが今回はWebサイトでのアニメーションの話です。この場合は基本的にはコードを書き、動きを定義していきます。
その実現方法として古くはFlash、その後はJavaScript、現在はCSSとJavaScript、場合によりCanvasやWebGL(GLSL)が用いられています。
なぜアニメーションが必要?
Webサイトにアニメーションが必要なのは何故でしょうか?
見栄えを良くするためでしょうか?
勿論それもありますがただそれだけではなく。操作できるものを示したり、ユーザーの操作を手助けするためにもアニメーションは非常に重要な役割を持ちます。
デスクトップでWebを見ている際のマウス操作を想像してください。サイト上には複数のリンクやボタン、バナー画像が存在します。もしこれらにカーソルを合わせても何もアクションがなかったらどうでしょうか?
ボタンに関してはデザインが適切であれば、見た目だけで操作できるものと気づくことができるでしょうが文中のリンクやバナー画像は操作できることに気づかないかもしれません。
次はメインビジュアルが画面いっぱいに広がるサイトを想像してください。スクロールできることが矢印などで示されていない場合、スクロールできることに少しの間気づけないかもしれません。こういった際には視線誘導するアニメーションがあるとユーザーの混乱を防ぐことができます。
操作できることを分かりやすくするのは最低限の配慮と言えるでしょう。
本当に最低限の対応って?
予算もなければ納期までの時間もない。そういう案件はありますよね。使い勝手なんて知ったもんじゃないという態度も取りたくなるものですが、ユーザーには予算も納期も関係ないので最低限配慮する必要があります。
/* 本当はタグ名でなく適切なクラス名をつけてください。以後省略します */
a {
text-decoration: underline;
}
a:hover { /* selector:hoverでマウスホバー時を表す */
text-decoration: none;
}
a img {
transition: opacity 0.1s;
}
a:hover img {
opacity: 0.7;
}
文字のリンクであれば下線のありなしの変化、画像であればマウスホバー時に若干透過をかける対応です。見栄えがいいかと言われると微妙なのですが操作できることは伝わるでしょう。細かいことを何も考えなくていない割にはそれなりに操作できることを分かりやすくできるので最低限の対応として使えます。
画像のリンクは透過する対応で大体の場合は操作可能であることが伝わると思いますが、まれに画像と背景色の都合で分かりにくい場合があるのでその場合は工夫が必要です。(上記サンプルのリンク画像 + 背景色を参照)
リンクに背景色を設定しておくことで、画像が透けたときにその背景色が見えるようになるので視認性が上がったり、多少サイトのイメージに合わせた印象に調整できたりします。
アンチパターン
たまにリンクではない画像にマウスホバーした際に画像が透過するサイトがあるのですがあれは個人的に良くないと思います。
(無意味なアニメーションによりユーザーが操作できると誤認する)どういう意図があるのかどの観点から考えても理解できないので見かけたらそのサイトは閉じていいと思います。有益な情報は多分ないです。
ちょっと丁寧な対応って?
正直に言えばサイトのデザインや印象によってアニメーションの付け方なども変わるので一概には言えません。
テキストリンクはマウスホバー時に下線を出す対応は割とそのままでも良いと思います。ちなみに出し方をこだわると実装が大きく変わります。
(後ほど説明する疑似要素を使う内容を応用すれば実装できます)
メニューを想定してみましょう
よくある帯状の横長のメニューを想像してください。
背景色があればその背景色の変化でアクションが行えることを伝えることができるでしょう。
Type 00
背景色がない場合はどうでしょうか?
背景色をつけることでアクションが行えることを伝えられるでしょう。
a {
transition: background-color 0.15s;
/* 背景色が変化する任意の秒数。sはsecond。msでmillisecondも使用できる */
}
a:hover {
background-color: 別の任意の色;
}
Type 01
a {
background-color: 任意の色;
transition: background-color 0.15s;
}
a:hover {
background-color: 別の任意の色;
}
では背景色の変化でしか表現はできないのでしょうか?
そんなことはありません。例えばの話ですがボーダーの変化で目を引くことができます。
Type02
この実装はここまでの実装とは大きく変わります。何が違うかというと疑似要素と呼ばれるものを使っています。雑な説明になりますが既存の要素の中に擬似的に要素があるように振る舞わせることができるものです。
今回の場合はリンク(aタグ)の下に疑似要素(::after)を用意し、背景色をつけてScaleY(縦の比率)を0にセットしておきます。これをマウスホバー時、ScaleY = 1の状態にすることでアニメーションを実現します。
a {
position: relative;
}
a::after { /* 疑似要素 */
position: absolute; /* 親を基準に絶対位置で配置 */
bottom: 0; /* 下から0px */
left: 0; /* 左から0px */
display: block;
content: "";
width: 100%;
height: 4px;
background: $color01;
transform: scaleY(0); /* スケールを0にする */
transform-origin: center bottom;
transition: transform 0.15s;
}
a:hover a::after {
transform: scaleY(1); /* スケールを1にする */
}
座標指定のleftは横幅いっぱいなので必要ないのでは?と思う方もいるかも知れませんが、クロスブラウザ対応を考えたときに指定していないと予想外の動作を行うブラウザがあったりもするので指定しておくとトラブルが減るでしょう。
(今回のサンプルは書きなぐったものなのでクロスブラウザもデバイス対応も大して考えていません)
疑似要素を使うことを覚えるとかなり応用が効きます。
上記のサンプルを少し変更するだけでもかなり違う印象のアニメーションが作れます。
Type04
メニューについての話はここまでにします。
画像のリンクの話
前述した画像リンクの話ですが現状透過させることしか書いていませんでしたね。こんなのもありますよって例を置いておきます。
a {
display: inline-block;
overflow: hidden; /* 内容がはみ出た場合その部分を非表示にする */
}
a img {
transition: transform 0.4s, opacity 0.2s; /* 変形と透明度の変化にアニメーション */
transform: scale(1);
}
a:hover img {
opacity: 0.7;
transform: scale(1.1); /* リンクの中の画像をマウスホバー時少し大きくする */
}
要約するとマウスホバー時に画像をゆっくり少し大きくするアニメーションを追加しました。これは秒数を極端に大きくしたりしても成り立つので演出の幅があります。(パキっとした動きも上品な動きにもできる)
……飽きてきた + 時間がない!
は嘘なのですが、細かなところの説明に時間がいくらあっても足りないので、ローディングアニメーション的なものについて説明して今回は終わりにしようと思います。時間がないのは本当。
(JavaScriptをゴリゴリに絡めた話もできればよかったのですがまたの機会に。いつか。そのうち)
ローディングアニメーション
ローディングアニメーションがなぜ必要なのでしょうか?
ローディングアニメーションはローディング中で不完全な状態のコンテンツをユーザーに見せない目的や、ページが読込中であってまだ完了していないことをユーザーに伝える目的があります。
当然、目的によってデザインや内容が変わってきます。
何をするのか考え方を説明します。
画面を覆う要素を用意する
画面を覆う要素を閉じるCSSアニメーションを用意する
ロード完了を表す別のクラスを付与した際にこのアニメーションが走るようにする
JavaScriptを使ってページのロード完了時のイベントを取る
画面を覆う要素にロード完了のクラスをJavaScriptで付与する
アニメーションが走り、画面を覆う要素が消える
以上を実現すればOKです。
ポイントを説明します。
画面を覆う要素position: fixed (画面に対する固定表示)やz-index (要素の重ね順)などを使って作るといいです。スマホでサイト見てるときに画面についてくる鬱陶しい広告ありますよね? そいつがposition: fixedです。
難しい話なのでちゃんとWebサイト作らない限りは聞き流していいですが、z-indexは適当に設定するとカオスになるので、サイト設計の段階でレイヤー構造(パーツ同士の重ね順)を意識して設定した方がいいです。
<div id="js-loader" class="c-loader">
<p class="c-loader__text">某鯖アドベントカレンダー</p>
</div>
.c-loader {
position: fixed; /* 画面を覆う */
top: 0;
left: 0;
z-index: 1; /* 画面を覆う */
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
width: 100vw; /* 画面を覆う */
height: 100vh; /* 画面を覆う */
background: #399;
font-size: clamp(20px, 2vw, 30px);
font-weight: 700;
color: #fff;
}
画面を覆う要素につけるアニメーションですがここまで使用してきたtransitionではなく@keyframesで実装しています。
keyframesという名前の通りキーフレームになるCSSをいくつか設定し、その後使用した場所で指定する秒数でその間を補完したアニメーションが作れます。
@keyframes loading {
0% {
transform: translateX(0);
transform-origin: left center;
}
100% {
transform: translateX(100vw);
transform-origin: left center;
/* 左を基準に画面の幅分、右に移動する */
}
}
@keyframes loading-text {
0% {
transform: translateX(0);
transform-origin: left center;
}
100% {
transform: translateX(-100vw);
transform-origin: left center;
/* 左を基準に画面の幅分、左に移動する */
}
}
.c-loader.is-loaded {
/* 画面を覆う要素を0.7秒かけて右に移動。アニメーション終了時のCSSを維持する */
animation: loading ease 0.7s forwards;
}
.c-loader.is-loaded .c-loader__text {
/* 画面を覆う要素の中の文字を0.7秒かけて左に移動。アニメーション終了時のCSSを維持する */
animation: loading-text ease 0.7s forwards;
}
クラスc-loaderがついている要素にクラスis-loadedがついたとき
animation: loading ease 0.7s forwards;
animation: loading-text ease 0.7s forwards;
上記のアニメーションが走ります。
イージング(雑に言うと緩急)はeaseを使用し、0.7秒(0.7s)かけてアニメーションを実行するという指定です。
forwardsはアニメーション終了後にその状態を維持するという指定です。
これがないとローディングアニメーション終了後、また画面を覆う状態に戻ります。
JavaScriptはサンプルなのでシンプルにしました。
const loader = document.getElementById("js-loader"); // 画面を覆う要素を取得
const LoadedClassName = "is-loaded"; // ロード完了時に付与するクラス名
const delay = 800; // ロード完了後、アニメーションが走るまで若干待つためのミリ秒指定
// ページロード時のイベントに処理を追加する
window.addEventListener("load", () => {
// 指定ミリ秒後に特定の処理をさせる
setTimeout(() => {
// 画面を覆う要素にロード完了クラスを追加
loader.classList.add(LoadedClassName);
}, delay);
});
終わり! と思いましたがアニメーションの説明だけしておきます。
なぜ文字を反対方向に動かすかについて。文字を入れた画面を覆う要素を右に動かしているのでそのまま文字も右に動いていってしまいます。
(電車に乗ってる人も合わせて同じ方向に移動してしまうイメージ)
これを防ぐため、文字を同じ速度で反対に動かすことで真ん中の位置に表示を保ちます。色々実装方法はありそうですがこんな感じでいいんじゃないでしょうか。
電車でいうと中の人が反対方向に同じ速度で走るのでその場所にとどまっているように見えるということですね。
上記に合わせて画面を覆う要素にoverflow: hiddenをかけているので、要素からはみ出た文字が消されていくような表現になります。
おわりに
長々と書きましたが全然書き足りませんでした。Webアニメーションやその実装が奥深いですよということが伝われば幸いです。
初歩的なこと(?)を適当に書いたので参考になるのかイマイチ分かりませんが何かの役に立つこともあるかもしれません。分からないことや気になることがあれば個人的に声をかけていただければ何か答えられるかも。
では記事は?
ということで20日の記事はここで終わり。
21日の記事の方にバトンパスとさせていただきます。
この記事が気に入ったらサポートをしてみませんか?