【HTML/CSS】たかがフェードイン/フェードアウトするだけの挙動に全力で取り組んだ結果、最強のCSSができてしまった話【最強】

エウレカのPairsグローバル事業部エンジニアの山内です。
この記事は、eureka Advent Calendar 2017 13日目の記事です。


昨日は新卒エンジニア鈴木康文さんの「Goのフレームワーク“Revel”を触ってみた」でした。
これからGoをはじめてみようという方にはとても入っていきやすい内容です。是非。


# 前置き

最近、HTML要素をフェードイン/フェードアウトする実装をしました。


たかがフェードインアウト、と侮るなかれ。
「CSSだけで表現」「JavaScriptは極力関与しない」「とにかく簡潔に」と決めて取り掛かるといくほどか難易度がアップします(しました)。


実装を終え、なるほどなぁと思うところがあったのでそのことを書くことにしました。
よろしければお付き合いください。


# 達成条件

以下を満たす、フェードイン/フェードアウトを実装します。

  • アニメーションをスタートするタイミングはJavaScriptで制御
  • アニメーションへの関与は .is-show の付け外しのみ
  • アニメーション自体はCSSだけで表現
  • ふわっと表示されて「ふわっと消える」
  • フェードアウトした要素はクリックとかできないようになっている
  • opacity:0 で透明にするだけではダメ

結果だけ知りたい方はこちら


# コラム:CSSアニメーションを理解するための2つのポイント

本筋から逸れますが、CSSアニメーションを理解する上で必要なことをまとめています。
不要な方はこの節を飛ばして # Try & Error へ。

  • CSSでいろいろできるんだろうけど、いまいち実装のイメージが湧かない
  • 調べてみると、 transform とか transition とかややこしい

そんな方はいらっしゃいますでしょうか。
だいじょうぶです。以下、2つのポイントだけ抑えれば、CSSアニメーションは誰でもカンタンに作れます。


① アニメーション定義の手段 — animationとtransition

アニメーションさせるためのプロパティは2つしかありません。
animationtransition です。
役割が異なるので、ユースケースを抑えることが理解の1歩目となります。

animation

@keyframe で fromからtoにかけてスタイルの状態変化 my-fade-in を定義し、それを.boxのアニメーションとして指定しています。


animationで抑えたいポイントは
スタイルの状態変化がfrom〜toへ一方通行であり、逆へ変化はできない という点です。


例えばフェードアウトさせるためには、逆の状態変化をkeyframeとして定義する必要があります。

transition

「.boxのスタイルのうち、 opacity については状態の変化をアニメーションする」という指定をしています。


.boxに .is-show が付与されると opacity: 1 の状態になろうとしてその経過がアニメーションとして描画されます。


※ サンプルでは .is-show の付け外しをJavaScriptで行っています。


transitionで抑えたいポイントは
スタイルの状態変化は双方向であり、行ったり来たりの変化が可能 という点です。


つまり、もとに戻るアニメーションもできるということです。


まとめると

  • animation
  • スタイルが1度変化したら、もとに戻らなくてよいクリエイティブ向き
  • ランディングページでよくある、スクロールごとのフレームイン動作、など
  • transition
  • スタイルが往復の変化をしなければならないクリエイティブ向き
  • トグルボタン、ドロワーメニュー、など

② アニメーショントリガー

アニメーションの定義手段はわかりました。
次に、そのアニメーションをスタートさせるトリガーについて考えます。


アニメーションが始まるトリガー・アニメーションを始めるトリガーは
大きく分けて3パターンです。

初回描画によるトリガー

ブラウザがHTML・CSSを読み込むと、定義されたスタイルの状態に変化する経過をアニメーションします。

JavaScriptを介したクラス操作によるトリガー

transitionサンプルのような、JavaScriptで .is-show 等を付け外しすることでスタイルの状態を変化させます。

擬似クラスによるトリガー

:checked :hover など、HTML・CSS仕様としての擬似クラスをアニメーショントリガーとして利用することもできます。


transitionサンプルと同じものを、JavaScriptなしで実現すると ↓こうなります。

inputの :checked を利用しています。
:checked になった時の隣接要素.boxの状態を定義しておけば、チェックボックスON/OFFに合わせて、opacityの状態変化をアニメーションしてくれるという仕組みです。


さらに、
<label for="xxx"> と、非表示にした <input type="checkbox" id="xxx"> 等を組み合わせることでクリエイティブの自由度も跳ね上がります。
(CSSオンリーのおしゃれなフォーム系ライブラリなどは、この仕組みで動いていることが多いです)


以上を抑えておけば、CSSでのアニメーションは思いのままです。
他サイトで気になったおしゃれなアニメーションの仕組みなども調べやすくなると思います。


# Try & Error

ここから本筋に戻り、考えたこと・試したことを並べていきます。

試行1: animationプロパティによるアニメーション

  • フェードイン用クラス、フェードアウト用クラスを定義
  • JavaScriptで2つのクラスを入れ替える方針
  • 無理やり感があってださい
  • 却下

試行2: transitionプロパティによるアニメーション

  • 状態が 表示←→非表示 を行ったり来たりするわけだから、transitionが最適
  • フェードの状態変化はopacityで行う
  • クリックできてしまう
  • opacity: 0 だけだと要素は残ったまま。
  • 却下

試行3: displayプロパティを追加

  • 要素が残っていることが原因なので、display:noneで消してしまえばいい
  • フェードアニメーションをしなくなった
  • transitionの切り替えよりもdisplayのONOFFが強く効いているみたい
  • 却下

試行4: visibilityプロパティにしてみる

  • クリックOK
  • フェードインOK
  • しかし、フェードアウトは「ふわっと消えない」
  • おしいけど却下。あと少し

# 最終的に

こうなりました。

特筆すべきは、
数値の変化ではないにも関わらず visibilityプロパティがtransitionのプロパティとして適用できる 点でしょうか。


(参考)
https://qiita.com/bukurocci/items/08b972e60a3b8357fc50


参考先にあるように、
timing-functionの計算中(=状態変化中)は visibility:visible 扱いとなるため、
.boxは画面上に描画されている状態となります。


そこに2つ目のtransitionとして opacity を指定することで
0 → 1 の状態変化がおこる = フェードイン・アウトのアニメーションが描画される
という仕組みです。


# 最終的に(追記)

はてぶで「iOS Safariで動作しない」とコメントをいただきました。ありがとうございます。
動く版を記載しておきます。

基本的な考え方は前述であっているはずですが、iOSSafariのCSSに勝利するためには
やはり一手間必要なようです。

iOS8のSafariで動いたので、Safari系は大丈夫な気がします。
MacChrome、AndroidChromeも上記で動いていました。


# 所感

最後にご紹介したCSSは、今後のフェードイン・フォードアウト実装では積極的に使っていこうと思っています。
最強なので。
visibilityについての知見はとても有意義でした。


今回の記事が、CSSを少しでも楽しむお役に立てばと思います。
みんなでCSSアニメーションいっぱいつくろう。


明日は、「この人を差し置いてPairsを語るなかれ。サーバサイド担当小島さん」の登場です。乞うご期待。

Like what you read? Give eureka_developers a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.