CSSでスムーズなアニメーションを実現する4つの新しい機能
Post on:2023年8月31日
今年もCSSの進化が止まりません!
先日紹介した表示・非表示をdisplayプロパティでアニメーションできるようになったり、スクロールをトリガーにしたアニメーションなど、新機能が登場しました。
今まではJavaScriptや複雑なCSSを使用しないと実装できなかった、スムーズなアニメーションを実現するCSSの4つの新しい機能を紹介します。
Four new CSS features for smooth entry and exit animations
by Una Kravets, Joey Arhar
下記は各ポイントを意訳したものです。
※元サイト様のライセンスに基づいて翻訳しています。基づいてというのは、貢献部分に関して同ライセンスも含みます。
- はじめに
- displayをキーフレームでアニメーションさせる
- 個別プロパティのトランジションを可能にする
- 最上位レイヤーとの間で要素をアニメーションさせる
- ビューのトランジションに関する注意点
- 終わりに
はじめに
モーション、サイトやアプリ上の動きはユーザーをあるインタラクションから次のインタラクションへと導くあらゆるデジタルエクスペリエンスの核となる部分です。しかし、現在のWeb上ではアニメーションに少しギャップがあります。たとえば、開始と終了のアニメーションを簡単にアニメーション化したり、ダイアログやポップオーバーなど閉じることができる要素と最上位レイヤーとの間でスムーズなアニメーション化したりする機能です。
それらのギャップをなくすために、Chrome 116および117ではCSSの4つの新しい機能が実装され、個別のプロパティに対してスムーズなアニメーションとトランジションが可能となります。
4つの新しい機能は、下記の通りです。
- キーフレームのタイムラインで
display
とcontent-visibility
をアニメーション化する機能(Chrome 116より)。 allow-discrete
キーワードを含むtransition-behavior
プロパティで、display
など個別プロパティのトランジションを可能にする機能(Chrome 117より)。- 開始のエフェクトを
display: none;
から最上位レイヤーにアニメーション化する@starting-style
ルール(Chrome 117より)。 - アニメーション中の最上位レイヤーの動作を制御する
overlay
プロパティ(Chrome 117より)。
display
プロパティをアニメーションさせるのは、以前の記事をご覧ください。
CSSのこの新機能、すごく楽しみ! UI要素にさまざまなアニメーションを簡単に実装できるようになります
displayをキーフレームでアニメーションさせる
2023年7月にリリースされたChrome 116から、キーフレームでdisplay
とcontent-visibility
を使用できるようになりました。キーフレームが発生すると、これらの値が入れ替わります。これをサポートするために新しい値を追加する必要はありません。
参考: CSSのこの新機能、すごく楽しみ! UI要素にさまざまなアニメーションを簡単に実装できるようになります
1 2 3 4 5 6 7 8 9 10 |
.card { animation: fade-out 0.5s forwards; } @keyframes fade-out { 100% { opacity: 0; display: none; } } |
動作はデモページでご覧ください。
※Chrome 116から
See the Pen
Fade out cards - Animation by coliss (@coliss)
on CodePen.
Chrome 116で見るのが面倒な人用に、デモの動作をGIFアニメで。
他の個別プロパティは、トランジションのイージング関数の50%で状態が切り替わります。ただし、display
とcontent-visibility
はトランジションの開始時または終了時に切り替わります。
上記のデモでは、0.5秒の継続時間に渡って不透明度を0にするアニメーションが行われており、その後display
をnone
にします。さらにforwards
キーワードはアニメーションが終了状態のままであることを保証するので、適用された要素はdisplay: none;
でopacity: 0;
のままです。
これはトランジションできることを示した簡単なデモです。ただし、トランジションで次のような複雑なアニメーションは作成できません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
.card { animation: spin-and-delete 1s ease-in forwards; } @keyframes spin-and-delete { 0% { transform: rotateY(0); filter: hue-rotate(0); } 80% { transform: rotateY(360deg); filter: hue-rotate(180deg); opacity: 1; } 100% { opacity: 0; display: none; } } |
spin-and-delete
は終了のアニメーションです。まずカードがY軸で回転し、色相回転が実行され、タイムラインの80%で不透明度が1から0に変化します。最後に、カードはdisplay: block;
からdisplay: none;
になります。
このような終了のアニメーションは要素に直接適用するのではなく、アニメーションのトリガーに設定できます。たとえば、クラスをトリガーにして、アニメーションを適用するボタンにイベントリスナーをアタッチします。
1 2 3 |
.spin-out { animation: spin-and-delete 1s ease-in forwards; } |
1 2 3 |
document.querySelector('.delete-btn').addEventListener('click', () => { document.querySelector('.card').classList.add('spin-out'); }) |
動作はデモページでご覧ください。
※Chrome Canary, Chrome 116から
See the Pen
Untitled by coliss (@coliss)
on CodePen.
Chrome 116で見るのが面倒な人用に、デモの動作をGIFアニメで。
上記のデモでは、終了時にdisplay: none;
になっています。さらに進めてアニメーションが最初に終了するようにタイムアウトを設定してDOMノードを削除したい場合もあると思います。
個別プロパティのトランジションを可能にする
キーフレームを使用して個別プロパティをアニメーション化する場合とは異なり、個別プロパティをトランジションさせるにはallow-discrete
でトランジションのビヘイビアモードを使用する必要があります。
transition-behaviorプロパティ
allow-discrete
モードは個別のトランジションを可能にするもので、transition-behavior
プロパティの値です。transition-behavior
には、normal
とallow-discrete
の2つの値があります。
1 2 3 4 5 6 7 8 9 |
.card { transition: opacity 0.25s, display 0.25s; transition-behavior: allow-discrete; /* 注意: これは必ずショートハンドの後に記述してください */ } .card.fade-out { opacity: 0; display: none; } |
transition
でショートハンドを使用する場合は、transition-behavior
は詳細度により適用されるようにそのショートハンドの後に記述してください。
動作はデモページでご覧ください。
※Chrome 116から
See the Pen
Fade out cards - Transition by coliss (@coliss)
on CodePen.
Chrome 116で見るのが面倒な人用に、デモの動作をGIFアニメで。
注: このデモは最初のデモと視覚的には似ていますが、異なるテクニックを使用しています。
transition
のショートハンド記法でもこの値が設定されるため、プロパティを省略し、代わりに各トランジションのショートハンドの最後にあるallow-discrete
キーワードを使用できます。
1 2 3 4 5 6 7 8 |
.card { transition: opacity 0.5s, display 0.5s allow-discrete; } .card.fade-out { opacity: 0; display: none; } |
複数の個別プロパティをアニメーション化する場合は、アニメーションさせる各プロパティの後にallow-discrete
を与えます。たとえば、下記のように記述します。
1 2 3 4 5 6 7 8 |
.card { transition: opacity 0.5s, display 0.5s allow-discrete, overlay 0.5s allow-discrete; } .card.fade-out { opacity: 0; display: none; } |
メモ: * {transition-behavior: allow-discrete}
を使用すると、常に個別アニメーションをオンにできますが、トランジションのショートハンドとの詳細度の衝突を防ぐために、記述の順番が後になっていることを確認してください。
開始アニメーションの@starting-styleルール
ここまでこの記事では、終了アニメーションについて解説してきましたが、開始アニメーションを作成するためには@starting-style
ルールを使用します。
@starting-style
を使用すると、要素がページ上で開かれる前にブラウザが参照できるスタイルを適用します。これは「開く前」の状態(アニメーションが開始する状態)です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/* 0. BEFORE-OPEN STATE */ /* Starting point for the transition */ @starting-style { .item { opacity: 0; height: 0; } } /* 1. IS-OPEN STATE */ /* The state at which the element is open + transition logic */ .item { height: 3rem; display: grid; overflow: hidden; transition: opacity 0.5s, transform 0.5s, height 0.5s, display 0.5s allow-discrete; } /* 2. EXITING STATE */ /* While it is deleting, before DOM removal in JS, apply this transformation for height, opacity, and a transform which skews the element and moves it to the left before setting it to display: none */ .is-deleting { opacity: 0; height: 0; display: none; transform: skewX(50deg) translateX(-25vw); } |
デモのTODOリストでは、アイテムの開始と終了の両方の状態が得られます。
※アニメーションはChrome Canary, Chrome 117から
See the Pen
Item transitions by coliss (@coliss)
on CodePen.
Chrome Canaryで見るのが面倒な人用に、デモの動作をGIFアニメで。
最上位レイヤーとの間で要素をアニメーションさせる
要素を最上位レイヤーとの間でアニメーションさせるには、open
の状態で@starting-style
を設定してどこからアニメーションさせるかをブラウザに伝えます。ダイアログの場合だと、open
状態は[open]
で定義できます。ポップオーバーの場合だと、:popover-open
疑似クラスを使用します。
ダイアログの実装は次のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* 0. BEFORE-OPEN STATE */ @starting-style { dialog[open] { translate: 0 100vh; } } /* 1. IS-OPEN STATE */ dialog[open] { translate: 0 0; } /* 2. EXIT STATE */ dialog { transition: translate 0.7s ease-out, overlay 0.7s ease-out allow-discrete, display 0.7s ease-out allow-discrete; translate: 0 100vh; } |
動作はデモページでご覧ください。
※アニメーションはChrome Canary, Chrome 117から
See the Pen
Dialog animation example by coliss (@coliss)
on CodePen.
Chrome Canaryで見るのが面倒な人用に、デモの動作をGIFアニメで。
次のデモでは、開始と終了のエフェクトが異なります。開始時はビューポートの下から上にアニメーションし、終了時はビューポートの上にアニメーションします。また、より視覚的にカプセル化するためにネストされたCSSで記述しています。
ポップオーバーをアニメーションさせるには、以前に使用したopen
属性の代わりに:popover-open
疑似クラスを使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
.settings-popover { &:popover-open { /* 0. BEFORE-OPEN STATE */ /* Initial state for what we're animating *in* from, in this case: goes from lower (y + 20px) to center */ @starting-style { transform: translateY(20px); opacity: 0; } /* 1. IS-OPEN STATE */ /* state when popover is open, BOTH: what we're transitioning *in* to and transitioning *out* from */ transform: translateY(0); opacity: 1; } /* 2. EXIT STATE */ /* Initial state for what we're animating *out* to , in this case: goes from center to (y - 50px) higher */ transform: translateY(-50px); opacity: 0; /* Enumerate transitioning properties, including display and allow-discrete mode */ transition: transform 0.5s, opacity 0.5s, display 0.5s allow-discrete; } |
動作はデモページでご覧ください。
※アニメーションはChrome Canary, Chrome 117から
See the Pen
Toolbar Popover Demo - Transition in and out, cleaner with nesting by coliss (@coliss)
on CodePen.
Chrome Canaryで見るのが面倒な人用に、デモの動作をGIFアニメで。
overlayプロパティ
最後に、popover
やdialog
を最上位レイヤーからフェードアウトするには、トランジションのリストにoverlay
プロパティを追加します。popover
やdialog
は祖先のクリップとトランスフォームをエスケープし、コンテンツを最上位レイヤーに配置します。overlay
をトランジションしない場合、要素はすぐにクリップされ、変形され、覆い隠された状態に戻り、トランジションが発生することはありません。
1 2 3 |
[open] { transition: opacity 1s, display 1s allow-discrete; } |
その代わりに、トランジションやアニメーションにoverlay
を含めることで、overlay
を他の機能と一緒にアニメーションさせ、アニメーション化するときに最上位レイヤーに留まるようにします。これにより、見た目がよりスムーズになります。
1 2 3 |
[open] { transition: opacity 1s, display 1s allow-discrete, overlay 1s allow-discrete; } |
動作はデモページでご覧ください。
※アニメーションはChrome Canary, Chrome 117から
See the Pen
overlay, dialog, popover, backdrop by coliss (@coliss)
on CodePen.
さらに、最上位レイヤーで複数の要素を開いている場合、オーバーレイは最上位レイヤーへのスムーズなトランジションを制御するのに役立ちます。この簡単なデモでその違いが分かります。2番目のポップオーバーにoverlay
を適用していない場合はトランジションを開始する前に、ポップオーバーは最初に最上位レイヤーに移動し、他のポップオーバーの後ろにジャンプします。これはあまりスムーズなエフェクトではありません。
ビュー トランジションに関する注意点
DOMに要素を追加したり削除したりするなどDOMに変更を加える場合、スムーズなアニメーションを実現するもう一つの優れたソリューションが、ビュー トランジション(view transitions)です。この記事ではビュー トランジションを使用したデモを2つ解説します。
1つ目のデモです。@starting-style
やその他のCSSトランフォームを設定する代わりに、ビュー トランジションを処理します。
まずは、CSSで各カードに個別のview-transition-name
を与えます。
1 2 3 4 5 6 7 8 9 |
.card-1 { view-transition-name: card-1; } .card-2 { view-transition-name: card-2; } /* etc. */ |
次にJavaScriptで、ビュー トランジションでDOMの変更(この場合はカードの削除)をラップします。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
deleteBtn.addEventListener('click', () => { // Check for browser support if (document.startViewTransition) { document.startViewTransition(() => { // DOM mutation card.remove(); }); } // Alternative if no browser support else { card.remove(); } }) |
これでブラウザは各カードの新しい位置へのフェードアウトとモーフィングを処理できるようになりました。
※Chrome 116から
See the Pen
Fade out cards - Transition -- Just VT by coliss (@coliss)
on CodePen.
Chrome 116で見るのが面倒な人用に、デモの動作をGIFアニメで。
2つ目は、リスト項目の追加と削除のデモです。この場合、作成したカードごとにユニークなview-transition-name
を与える必要があります。
※Chrome 116から
See the Pen
Item transitions with VT by coliss (@coliss)
on CodePen.
Chrome 116で見るのが面倒な人用に、デモの動作をGIFアニメで。
終わりに
この記事で解説した新しい機能により、Webプラットフォームでの開始および終了のスムーズなアニメーションにまた一歩近づきました。詳細については、下記のリンクをご覧ください。
sponsors