Posted at

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

ポップアップの表示/非表示アニメーションはフェードやスライドで作るのが一般的ですが、 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 を活用する、ただし機能しないことを念頭にアクセシビリティは担保する 」という結論に至りました。おしまい。