1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

ポップアップの表示/非表示アニメーションはフェードやスライドで作るのが一般的ですが、 clip-path や CSS Masks を使えば同心円状のワイプ(アイリスワイプ)も作れそう…作れました。つまりこういうことです。
clip-path バージョン
コミカルでポップなテイストのデザインに合いそうですね。
動くデモはこちら。

説明

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

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

後は面倒なのでコードを読んでください(ぶん投げ)。長いので clip-path バージョンのみ掲載します。
あ、 💬 の付いたコメントが今回の肝です。そこだけ読めば良いかと。
GitHub はこちら。

<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 の対応状況は割と複雑 なんですが、少なくともデモの mask バージョンは Chrome と Firefox でしか機能しませんでした。

  • Edge は全滅
    clip-path バージョンも mask バージョンもワイプされませんでした。 どうもこういうことらしいです。 もう知らない!プンプン!

おわりに

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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?