clip-path と CSS Masks でポップアップをアイリスワイプさせてみた

ポップアップの表示／非表示アニメーションはフェードやスライドで作るのが一般的ですが、 clip-path や CSS Masks を使えば同心円状のワイプ（アイリスワイプ）も作れそう…作れました。つまりこういうことです。
![clip-path バージョン](https://qiita-image-store.s3.amazonaws.com/0/248832/e49f1595-fd87-6e42-a083-c9953e428cdc.gif)
コミカルでポップなテイストのデザインに合いそうですね。
動くデモはこちら。

* [clip-path バージョン](https://mimonelu.github.io/sandbox/iris-wipe/clip-path.html)
* [mask バージョン](https://mimonelu.github.io/sandbox/iris-wipe/mask.html)

## 説明

* clip-path バージョン
ポップアップを丸いパス（ `clip-path: circle();` ）で切り抜き、パスのサイズをアニメーションさせています。

* mask バージョン
ポップアップに丸いマスク画像（実際は円形グラデーション）を適用し、マスクのサイズをアニメーションさせています。

後は面倒なのでコードを読んでください（ぶん投げ）。長いので clip-path バージョンのみ掲載します。
あ、 💬 の付いたコメントが今回の肝です。そこだけ読めば良いかと。
[GitHub はこちら。](https://github.com/mimonelu/sandbox/tree/master/iris-wipe)

```html
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Iris Wipe Popup with clip-path</title>
<style>

  * {
    line-height: 1;
    margin: 0;
    padding: 0;
  }
  :root {
    font-size: 5vmin;
  }
    body {
      background-image: repeating-linear-gradient(-45deg, #0070e0, #0070e0 16px, #0080f0 16px, #0080f0 32px);
      color: #ffffff;
      font-family: "Arial Black", "Avenir-Black";
      -webkit-font-smoothing: antialiased;
      overflow-y: scroll;
      -webkit-tap-highlight-color: transparent;
    }
      article {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        height: 100%;
      }
        h1 {
          font-size: 2.0rem;
          line-height: 1.375;
          margin: 0 1.0rem 2.0rem;
          text-align: center;
        }
        button {
          background-color: rgba(0, 0, 0, 0.1);
          border: 4px solid #ffffff;
          color: #ffffff;
          cursor: pointer;
          font-family: "Arial Black", "Avenir-Black";
          font-size: 1.0rem;
          outline: none;
          padding: 0.5rem 2.0rem;
          text-transform: uppercase;
        }
        button:hover {
          background-color: rgba(255, 255, 255, 0.1);
        }
      .popup {
        background-image: repeating-linear-gradient(45deg, #e02000, #e02000 16px, #f03000 16px, #f03000 32px);

        /* 💬 クリッピングされているため、 box-shadow のようなボックス外部に適用されるスタイルは描画されない */
        /* box-shadow: 4px 4px 32px 16px rgba(0, 0, 0, 0.25); */

        display: none;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        margin: auto;
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        width: 90vmin;
        height: 90vmin;

        /* 💬 丸いクリップパスを適用する */
        /* 💬 プリフィクスは Safari 用 */
        -webkit-clip-path: circle(50%);
        clip-path: circle(50%);

        /* 💬 矩形バージョン
        -webkit-clip-path: inset(0 0 0 0);
        clip-path: inset(0 0 0 0);
        */

      }
      .popup.open {
        animation: popup-animation 750ms ease-in-out 0ms 1;
        display: flex;
      }
      .popup.close {
        animation: popup-animation 750ms ease-in-out 0ms 1 reverse;
        display: flex;
      }

      /* 💬 クリップパスのサイズをアニメーションさせる */
      /* 💬 `clip-path: circle();` は transition できない。バグ？ */
      @keyframes popup-animation {
        0% {
          -webkit-clip-path: circle(0%);
          clip-path: circle(0%);

          /* 💬 矩形バージョン
          -webkit-clip-path: inset(50% 50% 50% 50%);
          clip-path: inset(50% 50% 50% 50%);
          */

        }
        100% {
          -webkit-clip-path: circle(50%);
          clip-path: circle(50%);

          /* 💬 矩形バージョン
          -webkit-clip-path: inset(0 0 0 0);
          clip-path: inset(0 0 0 0);
          */

        }
      }

</style>
<article>
  <h1>Iris Wipe Popup with clip-path</h1>
  <button onclick="openPopup()">Open popup!</button>
</article>
<aside class="popup">
  <h1>🎉 Popup! 🎉</h1>
  <button onclick="closePopup()">Close popup!</button>
</aside>
<script>
  const popup = document.querySelector('.popup')
  const openPopup = () => {
    popup.classList.add('open')
    popup.classList.remove('close')
  }
  const closePopup = () => {
    popup.classList.remove('open')
    popup.offsetWidth
    popup.classList.add('close')
    popup.addEventListener('webkitAnimationEnd', function () {
      popup.removeEventListener('webkitAnimationEnd', arguments.callee)
      popup.classList.remove('close')
    })
  }
</script>
```

なお、実際にプロダクトで使う際はバックドロップ（ i.e. `dialog::backdrop` 疑似要素 ）のスタイライズも必要になってくるでしょうから、ポップアップの DOM 構造は二重三重の入れ子にした方が無難です。

## ポイント

* `clip-path: circle();` が CSS Transition に対応していない
transition で `clip-path: polygon()` をモーフィングさせるデモはよく見かけるんですが、 `clip-path: circle()` はどうも未対応のようです。 もっとも、 CSS Animation で代用できるので問題はないと思います。

* `-webkit-mask-size` が CSS Transition に対応していない
もしかして上記共々バグなのかしら？しかしこれも animation で代用できるからセーフ、と思いきや…

* `-webkit-mask-size` が Safari で CSS Animation に対応していない
アウト。 [mask の対応状況は割と複雑](https://caniuse.com/#feat=css-masks) なんですが、少なくともデモの mask バージョンは Chrome と Firefox でしか機能しませんでした。

* Edge は全滅
clip-path バージョンも mask バージョンもワイプされませんでした。 [どうもこういうことらしいです。](https://stackoverflow.com/questions/39477755/how-to-make-clip-path-works-on-microsoft-edge) もう知らない！プンプン！

## おわりに

とりあえず、「 **mask は避け、 clip-path を活用する、ただし機能しないことを念頭にアクセシビリティは担保する** 」という結論に至りました。おしまい。