従来のWebアニメーションの問題を解決するAPI
Webにおけるアニメーションは、性質のまったく異なる4つのグループに分けられます。
- CSS TransitionsとAnimationsはパフォーマンスに優れ、キーフレームを提供しているが、構築に膨大な時間を必要とし、基本的な開始と終了の制御のみをCSSとJavaScriptで提供している。UIの応答、ループ、ページ読み込みなどのシンプルなアニメーションに利用される傾向がある
- SMIL(同期マルチメディア統合言語)はとても強力だが、非常に書きづらく、ブラウザーサポートが不完全だ。また、もっぱらSVGコンテキスト内の要素の制御に限られる
- JavaScriptは要素を直接制御できるが、キーフレームやイージングなどのデザイナーフレンドリーな関数はなく、ネイティブ最適化やCSSのパフォーマンスが欠けている。Canvas APIのアニメーションはすばらしいものの、アニメーションの基礎的な理解が必要であり、任意のDOM要素をアニメーション化できない
- GreensockのようなJavaScriptのアニメーションフレームワークは、JavaScriptでアニメーションするときの従来の欠点を補うものだが、ページの読み込み、パフォーマンス、新しい構文の学習といった、すべてのフレームワークが抱える短所がある
Web Animations APIは、これらの欠点を解消しようというものです。ネイティブのキーフレームとイージング、JavaScriptでの要素の制御、CSSと同等のパフォーマンスなど、最高の機能を1つの統一した仕様に統合しようとしています。コアコンポーネントはすでにChromeとFirefoxでサポートされており、SafariとEdgeでも開発が進行しているほか、堅牢なポリフィルが利用可能です。Webページをすばらしいものとするために、Web Animations APIについて真面目に考える、良い良いタイミングなのです。
The Web Animations API helps make animation a staple of web design, opening the gates to vendor-optimized performance and 3rd party tooling.
— Rachel?Nabors (@rachelnabors) September 29, 2016
シンプルなキーフレームアニメーション
キーフレームアニメーションのもっとも簡単な例として、赤いボールの要素がページの端から端へ移動するサンプルを見てみましょう。これから紹介するサンプルでは、手法に関わらず同じHTMLの要素を使います。
<div id="redball"></div>
最初に、アニメーションをCSSで実装すると、次のようになります。
body {
margin: 0;
background: #000;
overflow: hidden;
min-height: 100vh;
}
#redball {
background: red;
width: 30vmin;
height: 30vmin;
border-radius: 50%;
}
要素がビューポートのサイズに対応して常に対応するように、単位にはvminを使いました。
ボールをページの端から端へ移動させるのに必要なCSSは、次のようになります。
@keyframes moveBall {
from {
transform: translateX(-20vw);
}
to {
transform: translateX(100vw);
}
}
このアニメーションは、赤いボール要素の宣言から呼び出されます。
#redball {
animation: moveBall 3s infinite;
}
CodePenに表示される結果は次のとおりです。
イージング(開始で加速、終了で減速)が自動的に組み込まれていることなど、この時点でアニメーションについて注意すべき点がいくつかあります。
WebアニメーションAPIによる実装
HTMLと初期スタイルを維持しながら同じことを実現するために、CSSアニメーションを削除し、Web Animations APIを使ってJavaScriptに置き換えてみましょう。
var moveBall = document.getElementById('redball').animate([{
transform: 'translateX(-20vw)'
}, {
transform: 'translateX(100vw)'
}], {
duration: 3000,
iterations: Infinity,
easing: 'ease'
});
アニメーションの構文はCSSとほぼ同じですが、キーフレームを表す配列を持つオブジェクトとして表していることが分かります。キーフレームでtoやfromを明示的に宣言する必要はありません。宣言もできますが、JavaScriptが自動的にキーフレームを均等に割り当てます。
この時点ではアニメーションはまだ再生されません。CSSのように呼び出す必要があります。
moveBall.play();
なお、従来はtransitionsの値だったCSS translate構文は次のようなプロパティとして使えるようになりました。キーフレームの部分は今後のブラウザーでは、もっと簡単になります。
translateX: -20vw;
新しい構文はChrome Canaryではすでに提供されていますが、すべてのモダンブラウザーで使えるようになるまでには1年から2年ほどかかるでしょう。
スクリプトについて注意する点がほかにいくつかあります。
- JavaScriptは、CSSで標準的な「秒」ではなく「ミリ秒」単位でアニメーションのタイミングをとる(msがタイミング値の末尾にある場合はCSSでもミリ秒単位を利用できる)
- Web Animations APIは、(別々に定義されているときの)interation-countのCSSプロパティではなく、反復回数をiterationsとして指定する。キーワードはCSSのinfiniteではなく、繰り返し回数のInfinity(最初のIは大文字)
- Web Animations APIのデフォルトのイージングはlinearなので、デモではCSSアニメーションのデフォルト値をeaseと指定している
実行結果は、CSSアニメーションと基本的には同じです。
もちろん、JavaScriptでこのようなCSSアニメーションをコピーするだけでは、スクリプト言語のダイナミズムを最大限に発揮できません。そこで、フル機能のアニメーションを作成します。
画像を使った本格的なアニメーション
以前、私はテーブルの上でトランプを素早く動かすような、Webページ上で一連の画像を扱うアニメーション作成に興味がありました。従来のCSSで各カードに個別のアニメーションを描くのはかなり時間がかかり、結果はいつも同じようなものです。そこで、その代わりとなる最適なツールとして、Web Animations APIを使うことにしました。
プログレッシブカードのためのCSS
JavaScriptやWeb Animations APIが有効かどうかに関係なく画像を表示したいので、ページに一連の画像を追加することから始めます。
<div class="shuffle expose">
<img src="bridgefog.jpg" alt>
<img src="daisyface.jpg" alt>
<img src="drowninghand.jpg" alt>
<img src="firefigure.jpg" alt>
<img src="shellhand.jpg" alt>
<img src="waterfeet.jpg" alt>
</div>
コードを分かりやすくするため画像のalt値は空のままにしています。公開版では説明文が挿入されます。写真は.tafo.によるもので、Creative Commons Attribution-NoDerivs 2.0 Generic licenseのもとに使用しています。
JavaScriptが無効状態のときには、画像にごくわずかなアニメーションを加えるために、CSSを追加します。
@keyframes reveal {
to {
opacity: 1;
}
}
.shuffle {
min-height: 100vh;
position: relative;
}
.shuffle img {
width: 33%;
opacity: 0;
}
.expose img {
animation: reveal 1s forwards;
}
個々の画像の表示を次のように少しずつ遅らせます。
.expose img:nth-child(1) { animation-delay: 1s; }
.expose img:nth-child(2) { animation-delay: 2s; }
.expose img:nth-child(3) { animation-delay: 3s; }
…
当然ですが、これらの宣言はSassやほかのプリプロセッサーで自動化できます。
親要素にwebanimのclassがある場合、画像にはボーダーが表示されます。JavaScriptで次のようなスタイルが適用されます。
div.webanim img {
border: 1.4vw solid #eee;
}
JavaScriptの追加
ページの最後にスクリプトを追加します。アニメーションはランダムな値を必要とするので、次のように値を生成する関数を作成します。
function getRandom(min, max) {
return Math.random() * (max - min) + min;
}
インクリメント変数を設定して、画像のコンテナとその中のすべての画像を収集します。
var imgContainer = document.querySelector(".expose"),
imgSeq = document.querySelectorAll(".shuffle img"),
i = 1;
それから、コンテナにexposeクラスがある場合はクラスを削除します。画像を不透明度0のままにし、CSSアニメーションを非アニメーションするためです。
imgContainer.classList.remove("expose");
imgContainer.classList.add("webanim");
webanimクラスは画像にボーダーを入れて配置します。
スクリプトの大部分は関数内のforEachループで発生します。David DeSandroが作成した優秀なimagesLoadedスクリプトを使用し、(DOMに表示されるだけの画像ノードだけでなく)すべての画像データが読み込まれていることが確実になった時点で、関数を呼び出します。
function racknstack() {
Array.prototype.forEach.call(imgSeq, function(photo) {
setTimeout(function() {
photo.style.position = "absolute";
photo.style.width = getRandom(33, 45) + "%";
photo.style.left = getRandom(-5, 65) + "%";
photo.style.top = getRandom(-6, 60) + "vh";
var animate = photo.animate([{
opacity: '0',
transform: 'rotate(' + getRandom(-12, 12) + 'deg) scale(1.2)',
boxShadow: '0 0 12px 12px rgba(0,0,0,.3)'
}, {
opacity: '1',
transform: 'rotate(' + getRandom(-8, 8) + 'deg)',
boxShadow: '0 0 6px 6px rgba(0,0,0,.3)'
}], {
duration: 2000,
fill: 'forwards'
});
}, 1800 * i)
i++;
})
}
前述のように、手始めに次のような関数を呼び出す必要があります。
imagesLoaded(imgSeq, racknstack);
結果は次のようになります。
関数は次のように動きます。
- 各画像を絶対位置に設定
- 画像にランダムな幅を生成(パーセンテージを使用すると、可変でレスポンシブなまま画像を維持)
- 画像のランダムな位置を生成(実行できる負の位置を含む。ビューポートの端からわずかに見えるだけという可能性があるということ)
- Web Animations APIを使用して写真をアニメーション化。キーフレームは画像を回転させながら不透明度0から完全に表示されるまでフェードインする
アニメーションが完了した最終状態に画像を残したいので、forwardsへの入力が必要です。
ますます強力に
Web Animations APIの開発は急速に進んでいます。
- ChromeとFirefoxでサポートされているコンポーネントの増加で、現在のワーキングドラフトは着実に実装されている
- 仕様を実装していないブラウザーをサポートするため、依存性のない優れたポリフィルがある
- 仕様の可能性について熱心に主張するRachel Naborsが、探求に役立つCodePenのデモ一式とともにMozillaの開発者ブログのトピックで優れた導入を書いており、いくつかのカンファレンスでも発表している。彼女の取り組みの概要はこちら
最後に
Web Animations APIは、特殊で宣言型でステップバイステップのCSSアニメーションを、表現力とパフォーマンスに優れたアニメーションを可能にする動的で命令型のJavaScriptによるアプローチへ移行させるものです。
※本記事は、ゲストライターのDudley Storeyによるものです。SitePointのゲスト投稿では、JavaScriptコミュニティの著名な執筆者や講演者の魅力的なコンテンツの提供を目指しています。
(原文:Bringing Pages to Life with the Web Animations API)
[翻訳:柴田理恵/編集:Livit]