🌚

CSS だけでアニメーション付きのダイアログを作る

2021/01/02に公開

みなさん、こんにちは!めもりー(@m3m0r7)です。
正月の1日をかけて自分のポートフォリオを新しく作り直しました。

https://i.mem.ooo/

https://twitter.com/m3m0r7/status/1345292011121692673

私のポートフォリオは No JavaScript ということで JavaScript を一切使わないで実装をするというポリシーの元つくっています。
そこで、実際に実装したダイアログをどのように実装しているのかという点にフォーカスし、
今回は CSS だけでフェードイン/フェードアウトのアニメーション付きのダイアログを作る方法を備忘録として残しておきます。

アニメーションのないダイアログだったら簡単に実装できる

CSS にはクリックを判定するためのハックとして input:checked を使用する方法があります。
これを使うことで Siblings, つまり兄弟セレクタに対して何か施すことができます。

例えば下記の例を見てみてください。

JavaScript なしで checked であるかどうかを判定できています。
これを応用してダイアログを実装すると以下のようになります。

このように、アニメーションがなければなんの変哲もなく実装も容易です。
しかし、アニメーション付きで実装するには大きく問題があります。

アニメーション付きダイアログの問題点

アニメーションは基本、当該セレクタで実装した際に必ず一度発生します。フェードインとフェードアウトを実装するにあたって上記の例から .checkbox + .dialog にフェードアウトの処理 .checkbox:checked + .dialog にフェードインのアニメーションを指定してあげる必要があるわけですが、フェードアウトの処理がページアクセス時に発生してしまいます。
例えば以下のような CSS を期待します。

.dialog {
  position: absolute;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.8);
  color: #ffffff;
  padding: 40px;
  
}

.checkbox + .dialog {
  animation-name: dialog-reverse;
  animation-duration: 2s;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
  animation-direction: normal;
}

.checkbox:checked + .dialog {
  animation-name: dialog;
  animation-duration: 2s;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
  animation-direction: normal;
}


@keyframes dialog-reverse {
  from {
    opacity: 1;
    z-index: 1;
  }
  to {
    opacity: 0;
    z-index: -1;
  }
}

@keyframes dialog {
  from {
    opacity: 0;
    z-index: -1;
  }
  to {
    opacity: 1;
    z-index: 1;
  }
}

下記の例を見てみてください。(もし実行されてしまっていたら Rerun してみてください。)

※ アニメーションはわかりやすいようにあえて遅くしてあります。

このようにアクセス時に処理が走ってしまいます。
では、これをどのようにして解決すればよいのでしょうか。

アニメーション付きダイアログでキレイに見せる

少し考えたところではあるんですが、下記の図を見てみてください。

DOM のレイヤーが上記のようになっているため表示されています。
つまり、初回アニメーション時のみフェードアウト処理がユーザー側から見えなければよいのです。
ということは、下記のように予め表示すべき DOM を先端に持ってきておきます。

そして、一定時間後に DOM を正常な位置に戻してあげるようにすれば、フェードアウトの初回の処理を隠すことができます。

そこで本来表示すべき DOM のクラス名を main-contents と名付けておき、下記のように初回の DOM レンダリングの際にレイヤーの調整を行います。

.main-contents {
  /* ダイアログを隠せる大きさを指定します */
  min-width: 100vw;
  min-height: 100vh;
  
  /* ダイアログよりも上の z-index を指定します */
  z-index: 2;
  position: absolute;
  
  /* 背景色の指定は、ダイアログのアニメーションが見えてしまうため必須です!! */
  background-color: #ffffff;
  
  animation-name: init;
  animation-duration: 2s;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
  animation-direction: normal;
}

@keyframes init {
  99% {
    z-index: 2;
  }
  100% {
    z-index: 0;
  }
}

実装してみると下記のようになります。

いかがでしょうか。 CSS だけでアニメーション付きのダイアログを表示する処理ができました。
もう少し応用をすれば、スライドさせて表示したりといったこともできるかと思います。
プロダクション環境で使うかどうかは定かではありませんが…。

Discussion