ウェブ アニメーション - element.animate() を Chrome 36 で利用可能に

Brendan Kenny
Brendan Kenny

ウェブ上のアニメーションはかつて JavaScript の領域でしたが、モバイルへの移行に伴い、宣言型構文とブラウザによる最適化を実現するために、アニメーションは CSS に移行しました。モバイルで常に 60 fps を目標としている場合は、ブラウザが効率的に表示できる範囲を越えないことが理にかなっています。

JavaScript 駆動のアニメーションをより効率的にするためのツールは増えていますが、究極の目標は宣言型アニメーションと命令型アニメーションの統合です。アニメーションの記述方法は、どちらの形式で可能かではなく、最も明確なコードに基づいて決定されます。

Web Animations がその要請に応え、その最初の部分が element.animate() の形で Chrome 36 に導入されました。この新しい関数を使用すると、アニメーションを完全に JavaScript で作成し、CSS アニメーションや遷移と同じように効率的に実行できます(実際、Chrome 34 では、これらのすべてのメソッドはまったく同じ Web Animations エンジンによって駆動されます)。

構文はシンプルで、CSS の遷移やアニメーションを記述したことがある人なら、その部分は馴染みがあるはずです。

element.animate([
    {cssProperty: value0},
    {cssProperty: value1},
    {cssProperty: value2},
    //...
], {
    duration: timeInMs,
    iterations: iterationCount,
    delay: delayValue
});

この新機能の最大の利点は、スムーズでぎくしゃくしないアニメーションを実現するために、以前は必要だった面倒な手順を省略できることです。

たとえば、昨年のサンタ トラッカーでは、雪が降り続けるようにしたいと考え、CSS でアニメーション化することで効率的に処理できるようにしました。

ただし、雪の水平位置は、画面とシーン内で発生するイベントに基づいて動的に選択したいと考えました。もちろん、雪が降る高さ(ユーザーのブラウザ ウィンドウの高さ)は、実際に実行されるまでわかりません。そのため、ランタイムで CSS アニメーションを作成するとすぐに複雑になるため(数百個の雪片は数百個の新しいスタイル設定ルールを意味します)、CSS 遷移を使用する必要がありました。

そこで、次のようなアプローチをとりました。

snowFlake.style.transform = 'translate(' + snowLeft + 'px, -100%)';
// wait a frame
snowFlake.offsetWidth;
snowFlake.style.transitionProperty = 'transform';
snowFlake.style.transitionDuration = '1500ms';
snowFlake.style.transform = 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)';

キーは「フレームを待つ」というコメントにあります。遷移を正常に開始するには、要素が開始位置にあることをブラウザが認識する必要があります。方法はいくつかあります。最も一般的な方法の 1 つは、ブラウザにレイアウトの計算を強制する要素プロパティのいずれかから読み取る方法です。これにより、要素に開始位置があることをブラウザが認識し、終了位置に遷移する前に開始位置に移動します。この方法を使用すると、ブラウザ内部の優れた知識を自負しながら、キーを打つたびに不快な思いを抱くことができます。

一方、同等の element.animate() 呼び出しは、意図する内容を明確に示しています。

snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);

他にも多くのオプションがあります。CSS のアニメーションと同様に、ウェブ アニメーションは遅らせたり反復したりできます。

snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], {
    duration: 1500,
    iterations: 10,
    delay: 300
});

AnimationPlayer

element.animate() は実際には AnimationPlayer オブジェクトを返します。このオブジェクトは、Web Animations 仕様がさらにリリースされるにつれて重要性が高まります。JavaScript と CSS の両方で作成されたアニメーションには、関連する AnimationPlayer が設定されるため、有用で興味深い方法でシームレスに組み合わせることができます。

現時点では、AnimationPlayer には 2 つの機能しかありませんが、どちらも非常に便利です。アニメーションは、AnimationPlayer.cancel() を使用していつでもキャンセルできます。

var player = snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
// less than 1500ms later...changed my mind
player.cancel();

また、過去に CSS アニメーションやトランジションを基盤としたアニメーション システムを構築しようとしたことがある人にとっては、ウェブ アニメーションは完了時に常にイベントを発生させるため、安心して使用できます。

var player = snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
player.onfinish = function(e) {
    console.log('per aspera ad terra!');
}

試してみる

これらの機能はすべて Chrome 36 でリリースされ、本日ベータ版に移行します。試してみたい場合は、Chrome 36 のネイティブ実装をお試しください。ただし、Web Animations ポリフィルがあり、これにより、最新の Evergreen ブラウザで、Web Animations 仕様の大部分をサポートできます。

雪のエフェクトのデモでは、element.animate()ネイティブ バージョンと ポリフィルの両方を使用できます。

ご意見をお寄せください

ただし、これは今後の機能のプレビューであり、デベロッパーのフィードバックをすぐに得ることを目的としてリリースされています。すべてのユースケースに対応しているか、アニメーション用の現在の API の粗い部分をすべて削り落としたかは、まだ不明です。デベロッパーの皆様が実際にお試しいただき、ご意見をお聞かせいただくことが、この機能の正確性を確認する唯一の方法です。

この投稿へのコメントはもちろん貴重ですが、標準自体に関するコメントは、public-fx メーリング リストを通じて CSS ワーキング グループと SVG ワーキング グループに送信できます。

2014 年 10 月の更新: Chrome 39 では、play()pause()reverse() など、再生の制御に関連する複数のメソッドがサポートされるようになりました。また、currentTime プロパティを使用して、アニメーションのタイムライン内の特定のポイントにジャンプすることもできます。この機能の動作は、こちらの新しいデモで確認できます。

この投稿に協力してくれた Addy Osmani と Max Heinritz に感謝します。