Web Animations APIの基本的な使い方・まとめ

written by DEFGHI1977.

本文書はWeb Animations仕様が定義しているAPIを使ったアニメーションの作成方法について調査・試行錯誤したものをまとめたものです. スクリプトによるアニメーションは, いざ実現しようとすと考慮すべき点が多々ありどのように実装するか悩みがちです. そのため, ブラウザ側で標準的な仕組み(フレームワーク)を用意してくれることは意義のあることです. とは言えフレームワークを使いこなすにはその意図することころを理解しなければなりません. こういった経緯からAPI呼び出しに伴う“つまづき”を少なくするために実際に動作するサンプルを交えた解説ページを作ることとしました. 作者も手探り状態から少しずつ内容を掘り下げていますので内容には多くの間違いがあるかもしれません. 予めご了承の上ご利用ください. つーか, 仕様と説明との間のギャップが激しいAPIだと思う. 概念は単純なのに仕様に落としこむといちいち繁雑になるので, 仕様から概念に遡るのが異常に辛いという. 多分取りこぼしが沢山あるので, ぽつぽつ見直します…

※一部の仕様は筆者的に承服しかねる部分(KeyframeEffect/AnimationEffect)があり, 意図的に記述を省いているところがあります. 今後仕様が書き直されることを期待します.

サンプルコードについて

本ページのスクリプトは全てライブコードです(script要素の中身を直接表示しています). 従って期待通りに動作しているのであれば正しいコードです. なおFirefox49/Chrome51で動作確認をしていますが, 全てのコードが正しく動作するわけではありません(未実装なものが含まれている). なお, APIによっては細かなバージョン差異によっても動作しない可能性があります. 必ず自分の手で試してから導入を判断してください.

更新履歴

Web Animationsの前提知識

WEBを取り巻くアニメーション環境

インターネットが広く一般に普及したことにより, ユーザーの目を引くアニメーション表現はWEB環境において必須の技術になりました. しかしその実現方法にはこれまで統一的な手段があまりなく, WEBページの作り手がそれぞれ工夫を凝らしながらアニメーションを手作りしていました. その結果アニメーションに特化したJavaScriptライブラリが林立することとなり, ライブラリ間の互換性や相性の問題がWEB開発者の悩みの種となっています.

WEBブラウザのベンダもこの状況を静観していたわけではなく, WEB環境下でアニメーションを実現するための統一的な仕様を検討しており, CSS仕様においては既に多くの実績を結んでいます.

一方でHTMLの拡張(HTML5化)の過程で, 広範な(とりわけSVG関連の)仕組みを外部から取り込んだ事により, 様々な弊害が生まれました. HTMLではそれまでSVGから魅力的な(例えばtransform, mask, filterと言った)機能をHTML向けに再定義して利用していました. が, HTML5ではSVGそのものを仕様として取り込んだため, 同じ仕様が複数存在することとなり全体としての整合性が崩れてしまったのです.

そのため, WEB仕様を定めるW3Cでは現在HTMLにおけるCSSやSVGの仕様を一旦バラバラに分解し, HTML/SVG/CSSを包括する統一的な仕様への再構成(リファクタリング)に取り組んでいます. アニメーション仕様もこの流れに従い, 新たにWeb Animations仕様として再定義されることになったのです. また将来的にはアニメーションを取り巻く諸問題を一挙に解決すると言った野心的な一面も持ち合わせています.

WEB環境におけるアニメーションの実現法

現在WEBブラウザ上でアニメーションを表現する方法としては次の3つの方法が挙げられます.

当初はそれぞれが個別にアニメーションをモデル化しバラバラの仕様として存在していましたが, 将来的に全ての仕様がWeb Animations仕様を参照することで仕様の冗長性が排除されます.

仕様の相関

Web Animationsのアニメーション対象

Web Animationsは文書のビュー, すなわちスタイルに対するアニメーションについて定めています. つまり, DOMプロパティを書き換えると言った文書構造そのものに手を入れるようなアニメーションを想定していません.

補足)SVGアニメーションの扱いについて

かつてのSVGアニメーションでは任意のDOMプロパティをアニメーション化出来ため, 様々な問題を引き起こしていました. 例えば文書のリンク先の書き換えと言った処理をアニメーションで記述できました. しかし, 文書間のリンクを動的に変更することはユーザーの混乱を招きますし, 何よりSVGアニメーションを停止する手段が無かったため, SVGアニメーションをサポートする環境としない環境とで恒久的にリンク先が変化してしまうと言った奇妙な状況が発生しました. CSSやJavaScriptによるアニメーションであれば, ユーザー側で自発的に機能を無効とすることができます.

これらの問題はSVGアニメーション仕様の間違いというよりは, HTMLやCSSの陰で長らく実用上問題の出るケースについての検証が為されなかった事に起因しており, 中にはCSSアニメーションが言及していなかった破棄するには惜しいアイディアも含まれています. そのため, Web Animationsではこの内容を精査し, 取捨選択した上で新たなSVG Animations仕様として再定義することを目標の一つに掲げています.

Web Animations APIが提供する機能

Web Animations仕様はスクリプトを使ったWEBアニメーション作成をサポートするAPI(Web Animations API)を定義しています. このAPIは概ね次のような機能を備えています.

このように, アニメーションに必要となるひと通りの機能が提供されるため, 実装する側ではアニメーションの内容に注力することができるのです.

ただ, アニメーション構築フレームワーク的な側面が強いため, ある程度のAPIに対する習熟は必要になります. とは言え, 構造的に既存のアニメーション仕様とかけ離れたものではないため, CSSアニメーションやSVGアニメーションに触れた経験があれば, さほど苦労はしないはずです. むしろ, ECMAScript 6で導入されたPromiseオブジェクトやアロー演算子等の記法を前提としたデザインとなっているため, 文法的なつまづきに注意が必要です.

Web Animations APIが動作する環境

このように非常に有用なAPIですが, 残念ながら現状では利用可能な環境に乏しく互換性についても甚だ心細いところがあります. というのも, 仕様そのものを再構築しているためWEBブラウザ側の実装が追い付いていないからです. とりわけSVG Animations絡みの部分は仕様の不備から採用しないブラウザも多く, 今後もどうなるかわかりません. 従って, しばらくの間は各ブラウザベンダの動向に注目する必要があるでしょう.

ポリフィルライブラリ

Web Animations APIは本質的にJavaScriptで十分実装可能な内容です(多分). 従ってサポートしない環境でも同等の機能得るポリフィルライブラリが提供されています.

Web Animations APIの今後

現在検討されている仕様は俗に「Level1」とされていて, 将来的にはより高度なアニメーションを可能とする「Level2」へと継続的な仕様拡張が見込まれています. いずれにせよ, ある程度使い物となるにはもう少し時間がかかりそうです.

アニメーションの編集環境

Web Animationsを用いて本格的なアニメーションを構築する場合, 何らかのサポートツールを利用したいところです. CSSアニメーションについては最近のWEBブラウザ(Firefox, Chrome)であれば標準的にアニメーションデバッグツールが同梱されていますから, これを足がかりに設計を行うとよいでしょう. また, HTML5をベースとしたアニメーションオーサリングツールも徐々に増えています. 今後はWeb Animations APIをもサポートするツールの登場に期待が掛かります.

補足)WEBで見つかる文献について

Web Animations仕様は検討開始時からさほど長くない間にも何度も大きな書き換えを経験しており, WEBで見つかる資料・サンプルコードについては(興味深い内容であっても)大抵内容が古く使い物になりません. 従って, 仕様の調査を行う際は必ずW3Cが公開しているEditors Draftを最優先の文献として捉えて下さい. 不本意ながら本文書もいつ古遺物となるか判りません.

アニメーションの構成

Web Animationsが扱うモデル

Web AnimationsではWEB環境におけるタイムラインベースのアニメーションを表現するための仕組みを定義しており, アニメーションをタイミングモデル, アニメーションモデル及びアニメーションの対象の3つに分解して考えます.

タイミングモデル

タイミングモデルは, アニメーションを実行する上での各種時刻を扱う概念です. 例えばアニメーションを「いつ開始する」, 「どのくらいの長さで」, 「何回繰り返す」等の情報を扱います. この内容から実際のアニメーションが現在どのくらい進んでいるか(アニメーションの進捗)が得られます.

アニメーションモデル

アニメーションモデルでは, アニメーションの進捗を実際のグラフィックに適用する際のルール(キーフレーム)を定めます. 後はWEBブラウザ側でWeb Animations仕様に基づきキーフレーム間を自動補間(トゥイーン)します. 例えば「色が青から赤に変わる」アニメーションであれば, タイミングモデルから算出されたアニメーションの進捗状況に応じた色(例えば紫色)でグラフィックを描く事になります.

アニメーションの対象

アニメーション化の対象です. アニメーションモデルから算出したスタイルを元に対象を逐次描画します. なおアニメーション対象が存在しない(便宜上の)アニメーションも定義できます.

これらの仕組みを組み合わせることでWEBブラウザは現在描くべきグラフィックを判断し, その内容を書き換えることでアニメーションを表現します. このようなアニメーション機能の分割はFlashアニメーション等でおなじみのものです.

Web Animationsにおけるモデルの概略図

APIを構成するオブジェクト群

Web Animations APIは次のオブジェクト・インターフェースから構成されています.

Web Animations APIの構成図

アニメーションの実行

Element.animateメソッドを使ったアニメーション

では実際にWeb Animations APIを使ったアニメーションのサンプルを記述してみましょう. ここでは最も基本的なElement.animateメソッドを使ってdiv要素の描画位置をアニメーション化しています. アニメーションを行うにはキーフレームパラメータタイミングパラメータの2つが必要です.

このようにたった一行でアニメーションを記述できました. 同様のことを自力で実装した場合, より多くの記述が必要となることでしょう. その一方でanimateメソッドが一体何をしているのかについては, コードを見ただけではなかなか判断できません. これらの特徴からWeb Animations APIはフレームワーク的な色合いが濃いことがわかります.

Animation Element.animate(keyframes, options)
Animation CSSPseudoElement.animate(keyframes, options)
当該(擬似)要素に対してアニメーションを行います. アニメーションは自動実行されます. 得られたAnimationオブジェクトを使ってアニメーションを制御することが可能です.
keyframesキーフレームパラメータを指定します. 設定可能なパラメータについては後述します.
optionsタイミングパラメータを指定します. 設定可能なパラメータについては後述します.
returnこのアニメーションを表すAnimationオブジェクト

他のアニメーションライブラリとの比較

jQueryやSnap.svg等の大抵のJavaScriptライブラリではアニメーションを行う際に, 単一の値を渡すことで現在の値からアニメーションを開始出来ましたが, Web Animations APIは必ず初期値とアニメーション後の値の最低2つの値を渡す必要があります.

Animationオブジェクトを利用する

Element.animateメソッドはAnimateオブジェクトを生成します. このAnimationオブジェクトは, 使い捨てにせず何度も利用出来ます. 例を示します.

前の例では, ボタンをクリックする度にAnimationオブジェクトを生成していましたが, 今回は予めElement.animateメソッドを実行しておき, 得られたAnimationオブジェクトを操作することで, アニメーションを開始しています.

Animationオブジェクトの直接生成

AnimationオブジェクトはElement.animateメソッドを実行する他に直接生成する方法もあります. 事前に用意しておいたKeyframeEffectオブジェクトとタイムラインオブジェクト(document.timelineで得られる)とをAnimationコンストラクタに渡します. この過程で得られたAnimationオブジェクトはElement.animateメソッドの場合と異なり, 未実行状態になります. なお, KeyframeEffectコンストラクタに渡す内容はElement.animateメソッドに渡す内容と同等です.

KeyframeEffect(target, keyframes, options)
キーフレーム情報オブジェクトを生成します. Animationオブジェクトを生成する際に利用します.
target:Animatableアニメーション対象のElement/CSSPseudoElementオブジェクトを渡します.
keyframesキーフレームパラメータを指定します. 設定可能なパラメータについては後述します.
optionsタイミングパラメータを指定します. 設定可能なパラメータについては後述します.
DocumentTimeline document.timeline
タイムラインオブジェクトを取得します.
Animation(effect, timeline)
アニメーションオブジェクトを直接生成します.
keyframes:AnimationEffectキーフレーム設定オブジェクトを指定します.
timeline:AnimationTimelineアニメーションが属するタイムラインを指定します.
AnimationTimeline Animation.timeline
アニメーションを管理しているタイムラインを取得・設定します.

アニメーションを無効とする

アニメーション対象のスタイルに!importantが指定されていた場合, 当該CSSプロパティをアニメーション化することは出来ません.

アニメーションとDOM操作

アニメーションは対象のElementがDOMツリーに存在していなくても実行可能かつ継続されます. また, ノードの複製を行った場合はアニメーションを引き継ぎません.

キーフレームパラメータの記法

アニメーション化可能なプロパティ

Element.animateメソッド等に渡すキーフレームパラメータは, アニメーション対象のプロパティがいつどのような値をとるかについての情報をJSON形式にまとめたものです. アニメーション対象のプロパティはアニメーション対象ElementのCSSプロパティであり, DOM属性値をアニメーション化することは出来ません. なお, プロパティ名はキャメル形式(background-imageプロパティであれば, “backgroundImage”)で指定します.

概ね数値, 色及びそれらのリストとして記述できるCSSプロパティはアニメーション化可能ですが, 実際の処どのプロパティがアニメーション化可能かについては実際に試してみない限り判りません. また, CSSに存在しないプロパティを指定した場合は無視されます.

アニメーションとwill-changeプロパティ

CSSプロパティによるアニメーションを行う際, 頻繁に値を書き換えることをwill-changeプロパティでブラウザに通知することが出来ます. すると本プロパティをサポートする環境であれば, アニメーションに伴うグラフィック書き換え時の負荷が軽減されます. Web Animations APIを使った場合は, この通知が自動的に行われるためwill-changeプロパティを書き換える必要はありません.

SVGグラフィックのアニメーション化

SVG2ではSVG1.1SEからプレゼンテーション属性(CSSで記述できる属性)が拡張されており, 単純な属性であればCSSプロパティによる記述が可能となりました. 従ってElement.animateメソッドを使ってアニメーション化することが可能です.

CSSアニメーション化可能な属性
SVG2においてプレゼンテーション属性に加わった属性
cx, cy, height, width, x, y, r, rx, ry, transform(patternTransform/gradientTransform), d

キーフレームに対するプロパティ値の指定

アニメーション対象のプロパティに対しては特定のアニメーション進捗度(キーフレーム)に応じたプロパティ値のリストが必要です. Web Animations APIはプロパティ毎の書式に応じて, その内容を進捗度に応じた値で自動補間します. 例えば次のようなコードが与えられたとしましょう.

この場合, 「アニメーション進捗が0%の場合にleftの値を0pxに, 進捗が100%の場合に100pxにする」ことを表し, その間は2つの数値を補間した内容がleftに設定されます. 従って進捗が30%の時はleft値は30pxになります.

キーフレームの自動算出

プロパティ値のリストが与えられた場合, アニメーション進捗度を等分したものがキーフレームとして扱われます. つまり次のようなコードが与えられた場合は, それぞれ進捗度「0%, 25%, 50%, 75%, 100%」時のプロパティ値が与えられたことになります. なおキーフレームの算出方法はタイミングパラメータのspacing設定で変更することが可能です.

パラメータJSONの記法

キーフレームパラメータには複数のプロパティに対する設定を一度に含めることが出来ます. 例えば次のような2次元パラメータを渡す事が出来ます.

キーフレームパラメータの2次元構造
アニメーション進捗
0(0%)0.5(50%)1(100%)
アニメーション化
対象プロパティ
propAA0A50A100
propBB0B50B100
propCC0C50C100
propDD0D50D100

Web Animations APIではこの2次元パラメータをJSONコードに書き下す際に, プロパティ毎に記述(プロパティインデックス形式)してもキーフレーム毎に記述(キーフレーム形式)しても良いことになっています. つまり, 下記の記述は同じキーフレームパラメータを定義しています.

この内, キーフレーム形式ではoffset, easingの設定を追加指定できます. 例を示します.

補足)プロパティインデックス形式での非互換性

プロパティインデックス形式ではプロパティ毎に取りうる値の配列のサイズを揃えてください. 仕組み上, 次のようなコードを記述できますが, 仕様ではそのようなケースを想定しないため, ブラウザ毎に動作が異なります.

offset値によるキーフレームの明示

キーフレーム形式でのパラメータ指定では, offset値を指定することでキーフレームを直接指定することが出来ます. その際offset値は0から順に並ぶように指定します. なおoffset値が未指定の場合は, 前後のキーフレーム値から自動算出されます.

また同じoffset値が連続した場合, 値のジャンプを表します.

キーフレーム間に対するイージング関数の適用

キーフレーム形式でのパラメータ指定では, easingプロパティにイージング関数を指定することでキーフレーム間のアニメーション進捗に重みを付けることが出来ます. なおイージング関数の詳細については後述します.

キーフレームパラメータの再利用

キーフレームパラメータはその形式に柔軟性を持たせているため, 解析にコストが掛かります. 従って同じパラメータを繰り返し利用する場合や複数のElementで共有する場合は, パラメータの解析結果をSharedKeyframeListオブジェクトに保管しておくことが可能です.

KeyframeEffectオブジェクトの利用

Animationオブジェクトの直接生成の項で見たとおり, キーフレームパラメータは直接JSON形式で渡す方法のほか, 先にKeyframeEffectオブジェクトを作ってから渡す方法がありました. このKeyframeEffectオブジェクトを使うとキーフレームに関わる情報の詳細が得られます.

sequece<ComputedKeyframe> KeyframeEffect.getKeyframes()
キーフレーム情報を取得します. †旧名称:getFrames
void KeyframeEffect.setKeyframes(keyframes)
キーフレーム情報を設定します. †旧名称:setFrames

キーフレーム情報の取得

getKeyframesメソッドを実行すると, 現在のキーフレーム情報がキーフレーム形式で得られます.

[ComputedKeyframe]
double ComputedKeyframe.offset
offset値を取得します.
double ComputedKeyframe.computedOffset
自動算出されたoffset値を取得します.
DOMString ComputedKeyframe.easing
当該キーフレーム間に適用されるイージング関数を取得します.

下の例では, KeyframeEffectコンストラクタに渡した内容を表に出力しています. computedOffset値が自動計算された内容で補完されていることがわかります.

自動算出されたオフセット情報
offsetcomputedOffseteasingアニメーション対象
lefttop

タイミングパラメータの記法

タイミングパラメータの構成要素

アニメーションにはキーフレームパラメータのほか, アニメーションの時系列に対する振る舞いを表すタイミングパラメータが必要です. このパラメータには次のプロパティから構成されるオブジェクトを指定します.

duration
アニメーション1ループの持続時間
iterations
アニメーションの繰り返し回数
iterationStart
1ループ内のアニメーションを開始する位置.
delay
アニメーションを呼び出した後, 開始するまでの遅延時間
endDelay
アニメーションが終了した後の完了までの遅延時間
fill
アニメーション動作の開始前・終了後における進捗値の取り扱い
direction
アニメーションの再生方向
easing
アニメーション進捗値の重み付け方法
spacing
キーフレームの自動算出方法
iterationComposite
ループ時のアニメーションの取り扱い
composite
(複数のアニメーションが動作していた場合の)アニメーション値の取り扱い
id
アニメーションの識別子用途不明

各プロパティの役割を図にすると次のようになります.

タイミングパラメータのプロパティ相関

なお, タイミングパラメータに単一の数値を与えた場合はduratoin値が与えられたものとして扱います. 以下, それぞれの意味合いについて見ていきます.

CSS Animations/SVG Animationsとの相関

タイミングパラメータは他のアニメーション仕様から概念を取り込んでいます. 以下にその相関についてまとめました.

CSSプロパティとの相関
タイミングパラメータCSSプロパティ名値の指定等SVGアニメーション属性
duration animation-duration単位秒(s)dur
iterations animation-iteration-count無制限の場合「infinite」を指定.repeatCount/repeatDur
iterationStart ---
delay animation-delay単位秒(s)-
endDelay ---
fill animation-fill-mode互換fill:remove/freeze
direction animation-direction互換-
easing animation-timing-function互換calcModeと
keySpline属性で表現
spacing --
iterationComposite --accumulate:none/sum
compsite --additive:replace/sum
- animation-name@keyframesの参照-
Animation.playStateanimation-play-state--

durationによるアニメーションの持続時間の指定

タイミングパラメータにdurationプロパティが含まれていた場合, アニメーションをその長さ(ミリ秒)で実行します. 0もしくは"auto"が与えられた場合, アニメーションは即時完了したことになります.

iterationsによるアニメーションのループ回数の指定

タイミングパラメータにiterationsプロパティが含まれていた場合, その回数分アニメーションを繰り返します. 同様にInfinityを指定した場合は特に指示されない限りアニメーションを繰り返し続けます. 未指定の場合は1として解釈されます.

ループ回数に1未満の端数を指定することもできます.

iterationStartによるアニメーションのループ開始位置の指定

タイミングパラメータにiterationStartプロパティが含まれていた場合, アニメーションを指定したループ回数の分実行済みの状態から開始します. なお, iterationStartの有無に関わらず, アニメーションはiterationsの回数分実行されます. 例えば, iterationStart:0.5, iterations:3とした場合, アニメーションは0.5ループ目から開始され, 3.5ループ目に終了します.

delayによるアニメーションの開始時刻の指定

タイミングパラメータにdelayプロパティが含まれていた場合, アニメーションの開始時刻をそのミリ秒分遅延します.

delayパラメータに負の値を指定した場合, 指定したミリ秒分アニメーションが進行したものとして開始されます.

endDelayによるアニメーションの完了時刻の指定

タイミングパラメータにendDelayプロパティが含まれていた場合, アニメーションの終了後もそのミリ秒分アニメーションの完了を遅延します.

endDelayパラメータに負の値を指定した場合, 指定したミリ秒分アニメーションの終了を待たずしてアニメーションを完了します.

fillによるアニメーション開始時・終了時の値の取り扱い

アニメーションによって変更された内容は, 通常アニメーションが完了した際に全て破棄されます. fillプロパティはこのアニメーションに伴う値の取り扱いを表します.

[FillMode]
none
アニメーション開始前は何もせず, 終了後は直ちにアニメーション中の値を破棄する.
forwards
アニメーション開始前は何もせず, アニメーション終了後はその値を維持する.
backwards
delay値を伴うアニメーションの開始待ちの間はアニメーション開始時の値として扱う. 終了後は直ちにアニメーション中の値を破棄する.
both
forwards及びbackwordsを組み合わせたもの.
fill値によるアニメーション値の有効範囲

directionによる再生方向の指定

タイミングパラメータにおけるdirectionプロパティはアニメーションの再生方法及びループ方法を指定します.

[PlaybackDirection]
normal
通常のアニメーション再生です.
reverse
アニメーションを逆再生します.
alternate
1ループ毎にアニメーションを折り返します.
alternate-reverse
逆再生からはじめて1ループ毎にアニメーションを折り返します.

easingによるアニメーション進捗度の重み付け

タイミングパラメータにeasingプロパティが設定されていた場合, その内容を元にアニメーション進捗度にイージング関数による重み付けを行います. 詳しくは次のセクションで解説します.

SVG Animationsに由来するパラメータ

以下に示すspacing, iterationCompsite, compositeらはいずれもSVG Animations(SMIL)を起源とする仕組みであり, より高度なタイミング制御を行うためのパラメータです.

spacingによるキーフレームの自動算出

タイミングパラメータのspacingプロパティはoffset値が未設定のキーフレームを実際の進捗度に割り当てる際の方式を表します.

distribute
キーフレーム間を進捗度で等分するようにキーフレームを自動定義します. 規定値.
paced(left)
進捗度が確定しているキーフレーム間でアニメーションが等速(上り下りの傾きが一定)となるようにキーフレームを自動定義します.

例えば次のようなキーフレーム設定があったとします.

すると, spacingプロパティによって自動算出されるoffset値(つまりキーフレーム)が変化します.

spacingプロパティによるキーフレーム算出の違い

iterationCompsiteによるアニメーション値の累積

iterationCompsiteプロパティではループ毎のプロパティ値の変位を累積するかどうかを指定します.

[IterationCompositeOperation]
replace
ループの毎に値を元に戻します. 規定値.
accumulate
ループ毎に値を累積していきます.

compositeによるアニメーションの合成

compositeプロパティはアニメーション時に算出されたプロパティ値を既存のプロパティ値にどのように適用するかを指定します. addを設定することで複数のアニメーションが合成され, より複雑な動作を表現可能です.

[CompositeOperation]
replace
プロパティ値はアニメーション時の値で置き換えられます. 規定値です.
add
アニメーション時の値は現在のプロパティ値に加算されます(加法的アニメーション).
accumulate
アニメーション時の値は現在のプロパティ値に合成(累積)されます(累積アニメーション).
加法的アニメーション

加法的アニメーションの再現

現在APIレベルでの加法的アニメーションをサポートする環境はありませんが, アニメーション対象の構造を工夫することで再現することは可能です. アニメーション対象をdiv要素やg要素等のコンテナ要素で囲み, コンテナ要素・アニメーション対象の双方をアニメーションさせます.

後発アニメーションの優位性

composite値は規定値としてreplaceが選択されます. 従って同一プロパティに対するアニメーションは, 後から実行したものが既に実行済みのアニメーションを覆い隠します.

タイミングパラメータの取得

Animationオブジェクトを生成した際に設定したタイミングパラメータはAnimation.effect.timingから取得できます.

AnimationEffect Animation.effect
アニメーションの基本設定・現況を取得するためのAnimationEffectオブジェクトを取得します.
AnimationEffectTiming AnimationEffect.timing
アニメーションを構成するタイミングパラメータ情報を取得します.
[Animation.effect.timing]
readonly double AnimationEffectTiming.delay
delay値を取得します.
readonly double AnimationEffectTiming.endDelay
endDeray値を取得します.
readonly FillMode AnimationEffectTiming.fill
fill値を取得します.
readonly double AnimationEffectTiming.iterationStart
iterationStart値を取得します.
readonly double AnimationEffectTiming.iterations
iterations値を取得します.
readonly double/DOMString AnimationEffectTiming.duration
duration値を取得します.
readonly PlaybackDirection AnimationEffectTiming.direction
direction値を取得します.
readonly DOMString AnimationEffectTiming.easing
easing値を取得します.

一見意味のない機能に見えますが, CSSアニメーション等のAnimationコンストラクタを経由しないアニメーションの分析を行いたい場合に有用な機能です.

タイミングパラメータとメモリリーク

Animationオブジェクトの生成に際し下記パラメータを指定した場合, 時間経過によって無効とならない(なりにくい)アニメーションが作られます.

このようなAnimationオブジェクトは一度実行されるといつまで経ってもメモリから開放されません. なぜならアニメーションが終了しない, もしくは終了後もプロパティの値を書き換え続けているからです. 従って暗黙的にAnimationオブジェクトを生成するElement.animateメソッドによるアニメーションを何度も繰り返した場合, 古いAnimationオブジェクトが積み重なることでメモリリークが発生します. しかも, 先に見たとおり同じプロパティに対するアニメーションは後から実行したものが優先されるため, 問題が発生しても気付きにくいという性質をもっています.

メモリーリーク発生の原理

この問題はAnimationオブジェクトを再利用するか, アニメーション開始時に既存のAnimationオブジェクトをキャンセルすることで解決します.

イージング関数の定義

イージング関数とは

イージング関数(タイミング関数とも呼ぶ)はタイミングパラメータを構成するプロパティの一つで, アニメーションの進捗度に“重み”を付けることでアニメーションに表情を付けるために用います. イージング関数を適用せず, 単にプロパティ値の変位を時系列に沿って線形(linear)補間すると, アニメーションの印象は機械的な堅いものになります. ここでイージング関数を適用すると, グラフィックの変化が慣性の法則等の摂理に沿った動きに近くなり, 結果として自然な柔らかい印象を演出します.

イージング関数のグラフ

一般にイージング関数\(f\)は定義域\([0,1]\)から値域\([-∞,-∞]\)(但し\(f(0)=0\)かつ\(f(1)=1\)を充たす)への数値関数として定義されますが, 主に連続関数として定義する方法と離散関数として定義する方法の2つがあります. ここで連続関数とは値が滑らかに変化する(グラフがつながっている)ものを差し, 離散関数は値が跳び跳びとなる(グラフが階段状になる)ものを差します. 以下にイージング関数として指定可能なキーワードを示します.

Web Animationsが定義しているイージング関数
キーワード内容
連続関数 直線 linear線形補間(規定値)
曲線 cubic-bezier(x1,y1,x2,y2)3次ベジェ曲線による定義
ease弱加速・減速
cubic-bezier(0.25, 0.1, 0.25, 1)
ease-in加速
cubic-bezier(0.42, 0, 1, 1)
ease-out減速
cubic-bezier(0, 0, 0.58, 1)
ease-in-out加減速
cubic-bezier(0.42, 0, 0.58, 1)
ばね関数spring(mass, stiffness, damping, initialVelocity)ばねの振動による定義
Appleによる提案
離散関数 steps(count, start|end)階段関数(段数と遷移基準で指定する)
step-startアニメーション開始時に遷移
steps(1, start)
step-endアニメーション終了時に遷移
steps(1, end)

線形補間によるイージング関数

キーワードlinearが指し示すイージング関数は経過時間とアニメーション進捗値とを同じものと見做します. これはタイミングパラメータにおけるeasingプロパティ未指定の場合の規定動作です.

ベジェ曲線によるイージング関数の定義

Web Animations APIでは連続なイージング関数を3次ベジェ曲線を使って定義します. ベジェ曲線とは線の始点と終点の他に曲がり具合を表す制御点を使って表された曲線を指します. イージング関数の定義では横軸に経過時間\(x\)を, 縦軸にアニメーションの進捗度\(y\)をとり, そこで始点\((0, 0)\)と終点\((1, 1)\)を固定したベジェ曲線を作ります. その際にパラメータとして, 始点に対する制御点\((x_1,y_1)\)と終点に対する制御点\((x_2,y_2)\)の計4つの数値を指定することでイージング関数の形(グラフ)が決定されます. ここで\(x_1\),\(x_2\)は区間\([0, 1]\)の範囲に含まれる値です.

ベジェ曲線によるイージング関数

タイミングパラメータのeasingプロパティにこのイージング関数を渡す場合は書式をcubic-bezier(x1,y1,x2,y2)とします.

ベジェ曲線によるイージング関数の特性

ベジェ曲線によるイージング関数定義は少ないパラメータで多彩な動作を表現できる反面, 一般的に思い浮かべる(例えば \(y=f(x)\) と言った)関数の形からかけ離れています. 従って放物運動と言った単純なものであってもその軌跡を一旦ベジェ曲線に変換しなければならず, 必ずしも使い勝手の良いものではありません. そのため, イージング関数を自作する場合は何らかのジェネレータツールを利用するとよいでしょう.

また3次ベジェ曲線は本質的に3次曲線(関数)でありカーブは2箇所に限られます. 従ってそれより複雑な曲線を要する(例えばカーブが3箇所に及ぶ)イージング関数を定義することは出来ません. この場合, 後述する動きをキーフレーム毎に分割し複数のイージング関数を連結する方法をとるか, イージング関数を用いずに独自に計算したアニメーション値を元にオブジェクトを動かすようにします.

イージング関数のビジュアル化

ベジェ曲線によるイージング関数はSVGを用いると簡単にグラフ化出来ます. path要素が定めるパス図形の形状にイージング関数のベジェ曲線パラメータを宛てがうのです.

ビジュアル化したイージング関数

キーワードによるイージング関数の指定

先ほど見たとおりベジェ曲線によるイージング関数はイメージしにくいため, 実用度の高いものについては予め名称が付けられています.

キーワードによるイージング関数
キーワードcubic-bezierでの表現動作イメージ
linearcubic-bezier(0, 0, 1, 1)等速運動
easecubic-bezier(0.25, 0.1, 0.25, 1)初動は機敏に加速し, 徐々に減速する
ease-incubic-bezier(0.42, 0, 1, 1)だんだん加速していく
ease-outcubic-bezier(0, 0, 0.58, 1)だんだん減速していく
ease-in-outcubic-bezier(0.42, 0, 0.58, 1)だんだん加速し, だんだん減速する
キーワードによるイージング関数のグラフ

その他のイージング関数

Web Animations仕様が定めるイージング関数の他にも, 名称が付いているものはあります. 例えばEasing Function 早見表で紹介されているイージング関数群はjQuery Easing Plugin等で採用されており知名度のあるものです. この内Web Animations APIから利用可能なものはベジェ曲線として表現(近似)可能な単純なものに限られます.

ベジェ曲線で近似したイージング関数(早見表より抜粋)
名称 / 関数グラフ / cubic-bezier(x1,y1,x2,y2)
easeInSine easeOutSine easeInOutSine easeInQuad easeOutQuar easeInOutQuad
easeInCubic easeOutCubic easeInOutCubic easeInQuart easeOutQuart easeInOutQuart
easeInQuint easeOutQuint easeInOutQuint easeInExpo easeOutExpo easeInOutExpo
easeInCirc easeOutCirc easeInOutCirc easeInBack easeOutBack easeInOutBack

離散的な値をとるアニメーションとイージング関数

連続関数によるイージング関数は, アニメーション対象が連続的な値を取りうるもの(例えば, 位置や長さ, 色と言った数値で表されるもの)に対して適用されます. 一方, 設定値の切り替えによるアニメーションと言ったものも考えられます. 例えばvisibilityプロパティによるノードの表示を考えると, 表示する(ON)か表示しない(OFF)かと言った離散的な2つの状態しかありえません.

この場合, プロパティ値の切り替わり(OFF/ON)を0と1に宛てがうことでイージング関数として表現することが可能です. すると, いつ値を切り替えるかと言った点で, 経過時間0(つまりアニメーション開始直後)で値を設定するstep-startと経過時間1(アニメーション終了時)で値を設定するstep-endと言ったイージング関数のバリエーションが生まれます. 次の例ではイージング関数step-startstep-endの動作の違いについて確認しています.

離散的なイージング関数のグラフ

階段関数によるアニメーション

先ほどのstep-start/step-end関数では値のON/OFFと言った2つの状態(段数が1)を扱うためのものでした. これを任意段数に拡張したものがsteps関数です. こちらは数値プロパティに適用します.

steps関数の応用

steps関数は例えばスプライトシートによるアニメーション(いわゆる“パラパラマンガアニメーション”)に応用できます. まず, コマ毎のグラフィックを横に並べた画像(スプライトシート)を用意しこれを背景画像とします. ここで背景画像の位置をsteps関数を使ってアニメーション化することでパラパラマンガが完成します.

パラパラマンガのソース画像

なおアニメーションのコマ画像の位置さえ特定できれば, 画像の並べ方に制限はありません.

並べ方を変えたパラパラマンガのソース画像

同様に文字列を1行ずつ表示するサンプルを示します.

複雑な動作をキーフレームとイージング関数で表現する

単一のベジェ曲線として表せないアニメーションは, 複数のパーツ(キーフレーム)に分解しキーフレーム毎にイージング関数を適用することで表現します. その際, キーフレーム毎のプロパティ値を計算しても構いませんが, calc関数を用いて自動計算することが出来ます. つまり, パラメータとしてfrom値とto値に対する割合を指定することが可能です.

calc関数によるプロパティ値の自動計算

動きのライブラリ化

先ほどの例から判るように, 割合によるアニメーション表現はプロパティの種類やfrom, to値に依存していません. 従ってこの仕組みをライブラリ化することで, 動作を再利用することも出来ます.

なお応用次第で, CSSアニメーションのテンプレート化も可能です. この場合, from値とto値にCSS変数(とvar関数)を使うことが出来るでしょう.

アニメーション値の算出

基本的な補間ルール

キーフレーム間のプロパティ値はアニメーション進捗値0, 1に対応するfrom値とto値との間を線形補間したものとして得られます. アニメーション進捗値は経過時間にイージング関数を適用して得られた値を指し, 必ずしも0〜1の範囲に留まりません. また, タイミングパラメータにcomposite=add/iterationComposite=accumulateが指定された場合, 進捗値が累積していきます. それに伴い, 線形補間して得られたプロパティ値も指定した範囲から外れることがあります.

アニメーション進捗値とプロパティ値との相関

多くのプロパティではfrom値とto値の2つの値から算出した距離を元にアニメーション値を補間しますが, プロパティによっては特別な補間ルールが定められているものがあります.

calc関数の扱い

複数の数値から構成されるプロパティについては数値成分毎にアニメーション値が計算されますが, from値/to値にcalc関数が含まれていて, 見た目上複数の値から構成されるものについては計算結果を基準にアニメーション値が算出されます.

色のアニメーション

WEB環境では色をRGB色空間として管理しています. 色のアニメーションではこのRGB色空間を3次元空間に見立てて2つの色を補間します. これは色の指定にhsl記法を用いた場合も同様で, 一旦RGB値に変換した値を元にアニメーションが為されるため, 色相の回転によるアニメーションを表現することが出来ません.

色を扱うプロパティであっても, 色に対する変換を表現するものであれば別の結果となります.

座標変換のアニメーション

transformプロパティによる座標変換値をアニメーションする場合は, 一旦原始的な変換(rotate, translate, skew, scale, rotate等)成分に分解してから個々の成分毎にアニメーションが施されます.

離散値のアニメーション

数値として表せないプロパティ値については, 進捗値0.5を分岐点として0.5未満の場合にfrom値として, 0.5以上の場合にto値として扱います.

離散値に対する取り扱い

アニメーション補間値の取得

アニメーションによって算出されたプロパティ補間値はgetComputedStyleメソッドを使って取得したCSSStyleDeclarationオブジェクトから取得します. Web Animations APIによるアニメーションを別のスキームによるアニメーションと連携させる場合に利用します.

CSS値の計算機として利用する

CSS3ではcalc関数を使ってスタイル値を計算することが可能となりました. getComputedStyleメソッドを使うとこの計算結果を取得できますが, Animationオブジェクトと組み合わせることで更にこれらの線形補間値が得られます.

トランジションの動作をWeb Animations APIで再現する

CSS Transitionsによる動きはアニメーションの一種で, Web Animations APIで動作を再現することが可能です. 簡易的なフローを示します.

  1. getComputedStyleメソッドを使い, トランジション開始時(つまり現在)のアニメーション対象のスタイルを求めます.
  2. getAnimationsメソッドを使い, 現在動作中のアニメーションを終了します.
  3. 1で得たスタイル開始値を元にアニメーションを実行します.
  4. アニメーション開始時のタイミングパラメータにはfill:forwardsを指定します.

アニメーションの状態遷移

アニメーションの状態と遷移メソッド

Web Animations APIではアニメーションの状態をその実行状況から次の5つに分類しています.

Animationオブジェクトの状態
AnimationPlayState
状態名意味
idolアニメーションが呼び出されていない状態.
アニメーション設定は一切アニメーション対象に影響していません.
runningアニメーション実行中の状態(delay, endDelay中を含みます).
pausedアニメーションが一時停止中の状態.
finishedアニメーション完了の状態.
fill値がboth/forwardsに設定されていた場合は, アニメーション完了後も値が残ります.
pendingアニメーション状態遷移待ちの状態.

この状態はAnimation.playStateプロパティから調べることが出来ます.

readonly AnimationPlayState Animation.playState
アニメーションの現在の状態を取得します.

また, Animationオブジェクトには次の5つの制御メソッドが定義されており, これらの操作はAnimationの状態を強制的に変更する効果があります.

void Animation.play()
アニメーションの実行を開始します. アニメーションが一時停止中であれば再開します.
アニメーションはrunning状態になります.
void Animation.reverse()
アニメーションを逆再生します. 現在/前回の再生方向と逆方向に再生します.
アニメーションはrunning状態になります.
void Animation.cancel()
アニメーションの実行をとりやめ, アニメーション呼び出し前の状態に戻します.
アニメーションはidol状態になります.
void Animation.finish()
アニメーションの実行を最後までスキップし, アニメーションを完了時の状態にします.
アニメーションはfinished状態になります.
void Animation.pause()
アニメーションの実行を一時停止します.
アニメーションはpaused状態になります.

以上をまとめると次の図のようになります.

アニメーションの状態遷移

アニメーションの再生・逆再生

Animation.playメソッドを実行すると, アニメーションを再生します. 一時中断中であれば, アニメーションを再開します. Animation.reverseメソッドを実行すると, アニメーションを逆再生します. Animationオブジェクトでは, 現在のアニメーションの方向(順/逆)が管理されており, Animation.reverseメソッドはこの方向を反転します. 従って, reverseメソッドの後でplayメソッドを実行すると, アニメーションが逆再生されます. なお, いずれもアニメーション実行中には無効となります.

アニメーションの取り消し

Animation.cancelメソッドを実行すると, アニメーションの実行経過及び結果を含む全てを破棄し, 強制的にアニメーション実行前の状態に戻します.

アニメーションの一時停止・再開

Animation.pauseメソッドを実行すると, アニメーションを一時停止します. この後でplayメソッドもしくはreverseメソッドを呼び出すことでアニメーションの再生を再開することが可能です. なおreverseメソッドを呼び出した場合は再生方向が逆転します.

アニメーションの強制完了

Animation.finishメソッドを実行すると, アニメーションの状態にかかわらず, 強制的にアニメーション完了の状態にします. reverseメソッドの呼出有無いかんで, アニメーションの初期状態で処理が完了となることもあります.

アニメーション再生イベント

Animationオブジェクトは状態がfinished及びidolに遷移する際にそれぞれ“finish”イベント, “cancel”イベントを発生します. これらをアニメーション再生イベントと呼びます. 下記はこのイベントに対するリスナ関数を登録するプロパティです. この他addEventListenerメソッドを使ってリスナ関数を登録することも出来ます.

function Animation.onfinish
アニメーションが完了したタイミング(finishiイベント)で呼び出されるコールバック関数です.
function Animation.oncancel
アニメーションの実行がキャンセルされた際に呼び出されるコールバック関数です.

なお, アニメーションの再生開始や一時停止に伴うイベントは定められていません. なぜなら, これらの処理が呼び出されるタイミングはAPI呼び出しに限られるため, そもそもイベントによる通知が必要ないからです. また, アニメーション中であることの通知するイベントも存在しませんが, スクリプト側で能動的にアニメーション状況を取得することは可能です.

リスナ関数にはAnimationPlaybackEventオブジェクトが渡されます.

AnimationPlaybackEvent
アニメーション再生に関わるイベントオブジェクトです.
double AnimationPlaybackEvent.currentTime
イベントが発生したアニメーションの(再生速度を加味しない)経過時間. cancelイベント時には値が未設定となります. finishイベント時には値がアニメーション全体の持続時間(active duration)になります.
double AnimationPlaybackEvent.timelineTime
イベントが発生したアニメーションのdocumentTimelineにおける現在時刻.

イベントの独自定義

AnimationオブジェクトはEventTargetインターフェースを実装しているため, dispatchEventメソッドを用いて独自のイベントを着火することが可能です. 例を示します.

このようにアニメーション開始・中断にイベントを発生するようにすると, アニメーションの連携を行う際に便利です.

アニメーション再生イベントの応用

メインとなるアニメーションと空のアニメーションを同時実行することで, アニメーション途中の指定したタイミングで任意の処理を行わせることが可能となります. 例えば途中で効果音を鳴らすことが出来ます.

アニメーションとプロミス

アニメーションの非同期性

アニメーションは負荷の高い処理の一つです. 従って, ブラウザはアニメーションをメインスレッドとは別のスレッド(GPU)で行わせることがあります. しかし, スレッドを跨いだ処理の呼出にはタイムラグが付きものであり, サブスレッド側の準備が整う前にアニメーションが開始されてしまうと, そのラグの分アニメーションの内容がスキップされてしまいます.

例を示します. Animationオブジェクトを生成後, 片方は準備完了を待って, もう片方はそのまま再生を開始しています. アニメーションの実行を何度か繰り返すと, 直接アニメーションを際した方は完了待ちをしている方よりもアニメーションが先行することがあります. このことからアニメーションを直接再生すると, 先頭数フレーム分アニメーションがスキップする可能性があることがわかります.

そのため, Animationオブジェクトにはアニメーションの準備が整ったことを知らせるためのAPIが定義されています. 同様にアニメーションの終了を通知するAPIも定義されており, これらを使って非同期的に初期化されるアニメーションの再生を制御することが可能になります. アニメーションの同期に関わるAPIは次のとおりです.

Promise Animation.ready
アニメーションの準備が整った際に解決されるプロミスです. プロミス解決時に実行する関数には引数としてプロミスを解決したAnimationオブジェクトが渡されます.
Promise Animation.finished
アニメーションが完了した際に解決されるプロミスです. プロミス解決時に実行する関数には引数としてプロミスを解決したAnimationオブジェクトが渡されます.

プロミスの生成と解決のタイミング

Animation.ready及びAnimation.finishedプロパティはPromiseオブジェクトとして実装されています. Promiseオブジェクトにはthenメソッドが定義されており, Promiseオブジェクトを使う側でプロミスが解決(resolve)された際の処理を設定しておきます. その後プロミスが解決された(例えば初期化が完了した)とみなされるとthenメソッドに渡した処理が呼び出されます.

Animationオブジェクトを生成する, もしくはplay, reverse, pause, cancelメソッドのいずれかを実行すると, readyプロミスが新たに生成されアニメーションの状態がpendingに設定されます. その後, サブスレッド側の準備が整うと, readyプロミスを解決します. つまりAnimation.ready.thenに渡した処理が実行されます.

同様にAinmationオブジェクトを生成する, もしくはfinishメソッドを実行すると, finishedプロミスが新たに生成されアニメーションの状態がpendingに設定されます. その後, サブスレッド側の処理が完了すると, finishedプロミスを解決します. つまりAnimation.finished.thenに渡した処理が実行されます.

Animation状態とPromiseオブジェクトの相関

プロミスを使ったアニメーションの同時操作

非同期処理を扱うPromiseオブジェクトですが, 使い方によっては複雑な処理を簡単に記述することが出来ます.

アニメーションの同時開始

複数のアニメーションを同時に開始する場合, Promise.allメソッドを使ってreadyプロミスを一纏めにすることで記述が単純になります.

アニメーション完了判定

同様にfinishedプロミスを一纏めにすると, 全てのアニメーションが完了したことを簡単に確認することが出来ます.

アニメーションの取得

アニメーション中のAnimationを取得する

当該Elementにおいてアニメーションが実行中, 適用中かどうかは, Element.getAnimationsメソッドで判断できます. アニメーションが適用中とは, (fillプロパティ設定により)アニメーション完了後もアニメーション値が残っている状態を指します.

sequence<Animation> Element.getAnimations()
対象Elementにおいて実行中, 適用中のAnimationオブジェクトのリストを取得します. 逆にidol状態のAnimationは取得できません.

Animationオブジェクトが完了するかキャンセルされるとgetAnimationsメソッドの抽出対象から外れます. つまり(変数などにオブジェクトを保管していない限り)Animationオブジェクトにアクセスする手段が無くなり, アニメーションが破棄されます.

同様に, 当該documentにおいて実行中, 適用中のアニメーションのリストを取得可能です.

sequence<Animation> Document.getAnimations()
対象documentにおいて実行中, 適用中のAnimationオブジェクトのリストを取得します.

この機能はアニメーションに起因するメモリリークの有無の判断にも役立ちます. メモリリークが発生している場合, 本メソッドで得られるAnimationオブジェクトの数が際限なく増加していくからです. アニメーションの開始・終了の繰り返しに伴い数が増加する場合は, その処理内にAnimationオブジェクトの開放漏れが隠れています.

CSSアニメーションの取得

Element.getAnimations, Document.getAnimationsメソッドはスクリプトで生成したアニメーションの他に, CSSで定義したアニメーションも抽出対象です. 取得の条件としては次が挙げられます.

得られたAnimationオブジェクトを使ってCSSアニメーションを操作できます.

style.animationプロパティを直接操作したケースも同様です. CSSアニメーションに由来するAnimationオブジェクトは, CSSアニメーションが有効となった時点からgetAnimationsメソッドで取得できます.

CSSアニメーションに由来するAnimationオブジェクトもキャンセルすることが出来ます. するとアニメーションが破棄されgetAnimationsメソッドの抽出対象から外れます.

アニメーションの振る舞いと制御を分離する

アニメーションの定義をCSSで記述し, スクリプトはアニメーションの制御を行うと言った役割分担が可能です. 下の例では基本となるアニメーションは単一のkeyframes設定として記述しておき, 要素ごとのアニメーションの開始時刻を操作することで波状効果を得ています.

また, 一般的に長くなりがちなアニメーション定義が外部定義されるため, スクリプトコードの見通しが立てやすくなります.

CSSトランジションの取得

CSSアニメーションと同様にCSSトランジションも動作中であればgetAnimationsメソッドの抽出対象となります.

SVGに対するアニメーションの取り扱い

SVGアニメーション(animate, set, animateMotion, animateTransform要素によるもの)はアニメーション対象がCSSプロパティであったとしても(現状では)getAnimationsメソッドで取得できません.

なお, SVG要素であってもCSSアニメーションであればgetAnimationsメソッドの抽出対象となります.

アニメーション対象の取得

Animationオブジェクトのアニメーション対象はAnimation.effect.targetプロパティで取得します.

readonly Animatable KeyframeEffect.target
アニメーション対象を取得します.

CSSアニメーションが擬似要素(::before, ::after)に適用されていた場合は, 得られたAnimationオブジェクトのtargetプロパティにCSSPseudoElementオブジェクトが設定されます.

アニメーション再生の制御

アニメーションとタイムライン

アニメーションは時間の流れに沿って徐々にグラフィックの内容を書き換える概念です. タイムラインはその時系列を表す概念で, WEB環境ではdocument.timelineオブジェクトとして取得できます. アニメーションはこのタイムラインのどこかのタイミングに開始され, 設定されたシナリオ(タイミング・キーフレームパラメータ)に沿ってグラフィックを書き換えていきます. ここで, アニメーションの進み具合は伸縮(再生速度の設定)が可能で, 現在のグラフィックは現在時刻から算出されたアニメーションの再生位置から確定します.

タイムラインとアニメーションの関係

アニメーション再生に関わるAPI

Animationオブジェクトはアニメーションの再生そのものを制御するAPIを備えています. これを利用すると, アニメーションのシーク, 再生速度の変更等の操作を行えます.

double Animation.playbackRate
アニメーションの再生速度を取得・設定します.
double? Animation.currentTime
アニメーションの(再生速度を加味しない)再生位置を取得・設定します.
double? Animation.startTime
アニメーションの開始時刻を取得・設定します.

再生速度の変更

Animation.playbackRateプロパティはアニメーションの再生速度及び再生方向を表します. 値の取得だけでなく値を設定することも出来, アニメーションを任意倍の速度で再生することが可能です. また, 負値を指定すれば逆方向への再生となり, ループ再生時はループ回数が0となったタイミングでアニメーションが停止します.

再生位置の取得

Animation.currentTimeプロパティはアニメーションの再生位置を表します. 再生位置は(delay値を含む)アニメーション開始からの経過時間を指し, playbackRateの影響を受けない絶対的な値です. 下の例では, rangeインプットにアニメーションの再生位置を表示しています. playbackRateを2としアニメーションを倍速再生していますが, アニメーション再生位置は変化していません.

なおアニメーション値が計算できない場合(例えばアニメーションがidol状態の場合)はnull値が返されます.

再生位置のシーク

currentTimeプロパティに値を設定するとアニメーションの状態(runningとそれ以外)はそのままにアニメーションの内容を指定した再生時刻の位置までスキップします. 下の例ではrangeインプットと組み合わせて, シークバーによるアニメーションを表現しています.

これはアニメーションの進行状況をタイムラインから切り離し, 別のシーケンス管理機構に任せられることを意味しています. 詳しくは後述します.

システム時刻を基準としたアニメーション

時計などのシステム時刻(wall clock)を基準としたアニメーションを表現する場合, iterationsをInfinityに設定しcurrentTime値に経過ミリ秒数を設定します.

開始時刻の取得とアニメーションの開始スケジュール

Animation.startTimeプロパティはアニメーションが開始された時刻を表します. この値は通常, playメソッドやreverseメソッド等によるアニメーションが開始された時刻が格納されていますが, 値を書き込むことで, 指定した時刻にアニメーションが開始した/するように振る舞います. 従って, 過去時刻を渡せばアニメーションは現在時刻までスキップされ, 未来時刻を渡せばアニメーションはその時刻に開始されるようにスケジュールされます. なお, 未来時刻を指定した際もアニメーションの状態はrunningに設定されます. これは複数のアニメーションをシーケンシャルに開始する場合に便利な機能です.

AnimationオブジェクトのstartTimeを単独で操作する場合は, 必ずAnimate.timeline.currentTimeプロパティを使って現在時刻を取得して下さい. AnimationはAnimation.timelineを時系列として管理されているため, その他のTimelineないしはDate.newメソッドで得た現在時刻と整合性がとれません.

readonly double AnimationTimeline.currentTime
このタイムライン上の現在時刻を取得します.

アニメーション現況の取得と応用

アニメーションの現況

アニメーションの状況を知るために現在の(CSS)プロパティ値を取得したい場合, (window.)getComputedStyleメソッドを用います. ですがAnimate.effect.getComputedTimingメソッドを利用すると, アニメーションの進捗に関わる詳しい情報が得られます.

ComputedTimingProperties AnimationEffect.getComputedTiming()
アニメーションの現在の詳細情報を取得します.
double ComputedTimingProperties.endTime
アニメーションの終了位置を取得します. つまり, delay+activeDuration+endDelay値に一致します. ループ回数がInifinityの場合はInfinityが返されます.
double ComputedTimingProperties.activeDuration
アニメーションの(delay, endDelayを除いた)全体としての持続時間を取得します. つまり, duration×iterationCount値に一致します. ループ回数がInifinityの場合はInfinityが返されます.
double ComputedTimingProperties.localTime
アニメーションの再生位置を取得します.
double ComputedTimingProperties.progress
アニメーションの進捗値(0〜1)を得ます. この値にはeasing関数が適用済みです.
double ComputedTimingProperties.currentIteration
アニメーションの現在のループ回数を取得します.

なお, 得られたComputedTimingPropertiesオブジェクトはライブオブジェクトではありません. 従って, 刻々と変化するアニメーションの進捗値やループ回数等の値を取得する場合は, その都度getComputedTimingメソッドを実行する必要があります.

アニメーション終了位置の取得

endTimeプロパティからはアニメーションが完了する位置が得られます. 例えば前セクションで見たシークバーの実装において, rangeインプットの数値範囲の算出に利用できるでしょう.

アニメーション現在位置の取得

localTimeプロパティからはアニメーションの現在の位置が得られます. endTimeプロパティと組み合わせると, アニメーションの再生の進み具合が割合で得られます.

アニメーション進捗値と周回値

progressプロパティからはcssプロパティ値の補間位置(0〜1)が得られます. アニメーションが未開始の場合や, delay期間中はアニメーション値が存在しないためnullが返されます.

同様にcurrentIterationプロパティからは現在のアニメーションの周回数(0〜)が得られます.

これらの値はアニメーションの連携を行う上で重要です. 例えばこれらの値を元にグラフィックを描くことで, 自作のアニメーションをAnimationオブジェクトの制御下に置けるからです. 詳しくは次のセクションで解説します.

Animationオブジェクトの活用

アニメーションの分割

Web Animations仕様ではアニメーションをタイミング(タイミングモデル)とアニメーション値算出・設定(アニメーションモデル)の2つに分解していました. Animationオブジェクトはこの2つの仕組みを合成したものですが, 機能毎にAnimationオブジェクトを分割することが出来ます. 方法は次のとおりです.

  1. タイミングのみを管理するAnimationオブジェクトを生成します. duration, iterations, direction等のプロパティはこちらで設定します. キーフレーム情報は必要ありません.
  2. アニメーション値のみを管理するAnimationオブジェクトを生成します. キーフレーム(from値とto値)設定はこちらで定義します. タイミング設定のduration値は1(つまり, currentTime = progressとする)に設定します.
  3. アニメーションの制御は1のタイミング管理Animationオブジェクトに対して行います.
  4. タイミング管理Animationオブジェクトのplay(reverse)メソッドを実行したら, progress値を元にアニメーション値管理AnimationのcurrentTimeを変更します.

こうすることで, 次のようなアニメーションカスタマイズのバリエーションが生まれます.

アニメーションの分割

タイミング管理の外部移譲

アニメーションの実行をAnimationオブジェクトそのものに任せるのではなく, currentTimeプロパティの随時書き換えで表現するものです. その際の再生位置の参照先としては, 次のようなシーケンス管理機構が考えられます. Animationオブジェクトにはキーフレーム情報とduration値のみを渡します.

メディアとアニメーションを連携する

アニメーションと同様に時系列に沿った変化を表すものとしてはメディアがあります. HTMLAudioElementやHTMLVideoElementにはAnimationオブジェクトと同様に再生位置を取得するAPIを備えているため, アニメーションをこの値に合わせることでメディアとアニメーションとを連携させることが出来ます.

アニメーション描画処理の外部移譲

Animation.effect.getComputedメソッドから得たprogress/currentIteration値を元にグラフィックを描く処理を記述すると, タイミングの制御をAnimationオブジェクトに任せることができます. 従って使い慣れたグラフィック描画ツールを用いたアニメーション表現が行えます.

ここではcanvas要素を使った例を示します.

自作のイージング関数の適用

progress値を用いると, 単一のアニメーションをタイミングの管理とアニメーション値算出の2つのAnimationオブジェクトに分離出来ます. その際, 2つのAnimationオブジェクトの橋渡しをするprogress値に自作のイージング関数を適用することが可能です.

自作のイージング関数適用の概念図

例を示します. ここではmyEasing(\(y=x^2\))という名称のというイージング関数を設定しています.

自作のイージング関数は数値引数に対して数値を返すものであれば内部構造はどのようなものでも構いません. JavaScriptで定義した関数をそのまま利用するため, 精密な座標定義を伴うものや算術関数として表現できるものに適しています.

同期メソッドの定義・実行

アニメーションを複数に分割したことから, それらを同期する仕組み(sync関数)が必要となります.

sync関数を実行するタイミングには次のものがあります.

sync関数の呼び出しには必ずrequestAnimationFrameを用い, フレーム描画を継続するかどうかをアニメーションの動作有無(playStateプロパティ)で判断します. また操作対象がCSSプロパティの場合, will-changeプロパティによるヒンティングも組み合わせます.

パスとアニメーション

パス図形について

SVGがHTMLに統合されるにあたり, 図形の形状を表す概念が新たに導入されました. これをパス図形と呼び, 図形の外形をパスコマンドと呼ばれるパラメータ片を組み合わせることで任意の図形を表現します. パス図形を表すパス文字列は現在様々な場面で活用されており, CSSではモーションパスの定義で利用しています.

SVGのパスコマンド一覧
コマンド内容記述
M/m始点に移動するM[x],[y]
L/l直線を引くL[x],[y]
H/h水平線を引くH[x]
V/v垂直線を引くV[y]
C/c3次ベジェ曲線を引くC[x1],[y1] [x2],[y2] [x],[y]
S/s3次ベジェ曲線を引くS[x2],[y2] [x],[y]
Q/q2次ベジェ曲線を引くQ[x1],[y1] [x],[y]
T/t2次ベジェ曲線を引くT[x],[y]
A/a楕円弧を引くA[rx],[ry] [angle] [largeArcFlag] [sweepFlag] [x],[y]
Z/zパスを閉じるZ
小文字のコマンドは現在位置からの相対座標として扱います.

モーションパスによるアニメーション

モーションパスとはオブジェクトの軌跡を表す概念で, 図形の形状に沿ってオブジェクトを動かす際の基準となるものです. Web Animations仕様の初期草案段階にはこのモーションパスによるアニメーションがありましたが, 現在はCSSのMotion Path Module仕様として分離されたことでCSS記述だけで実現できます. 例を示します.

この機能はもともとSVGのanimateMotion要素の仕組みを再構成したもので, モーションパスをSVGのパス文字列として定義します.

モーションパスアニメーションに関わるCSSプロパティには次の3つがあり, motion-pathプロパティでオブジェクトの動きを定義し, motion-offsetプロパティをアニメーション化することでオブジェクトをパスに沿って動かします.

motion-path
オブジェクトの軌跡を表します. path('パス文字列')もしくはそれに類するものとして記述します.
motion-offset
オブジェクトの配置位置を表します. 長さ値/パーセント値で指定します.
motion-rotation
オブジェクトの回転の有無を指定します. [auto|reverse] (xxdeg)で指定します.

モーションパスアニメーションをAPIで操作する

先ほどのCSSによるアニメーションをElement.animateメソッドで書き換えると次のようになります.

モーションパスアニメーションの再現

現在モーションパスアニメーションはBlink系の一部のブラウザでのみ動作しますが, SVGDOMと組み合わせることで動作を再現することが出来ます. SVGPathElementを使うと, パス上の道のりに対する座標が得られます. 従って, Animationオブジェクトから得た進捗値から座標値を求め, アニメーション対象の描画位置をtransformプロパティで変化させるのです.

モーフィングアニメーション

SVGのpath要素をアニメーション化することで, 図形のモーフィング効果が得られます. その際はdプロパティをアニメーション化します.

モーフィングアニメーションを正しく動作させるには2つのパス文字列の構造が一致している必要があります.

モーフィングアニメーションの再現

モーションパスアニメーションと同様, (CSSによる)モーフィングアニメーションは現在Blink系のブラウザでのみ有効ですが, SVGDOMを利用することで動作を再現することが可能です. 元図形と変形先図形のパス文字列の構造を一致させておき, 頂点情報をprogress値で補間するのです.

アニメーションの集約

複数のアニメーションを一つにまとめる

アニメーションは単独で実行できるものですが, 複数のアニメーションを連携させて複雑で豪華な表現を行うことはよくあります. このようなアニメーションを制御する場合, アニメーションを個別に操作するのは余りに面倒と言えます. そこで, Web Animations APIでは将来的にGroupEffectとSequenceEffectの導入を検討しているようです. これらは複数のアニメーションを統合し, 単一のものとすることでアニメーションの制御を簡略化します.

アニメーションの集約

アニメーションの同時操作

GroupEffectオブジェクトを利用すると, 複数のキーフレーム設定を単一のAnimationオブジェクトに統合することが可能です. 一度の操作で複数のアニメーションを同時に操作することが可能となります.

GroupEffectオブジェクトの動作サンプル

アニメーションの直列化

同様に, 複数のアニメーションを連続して再生するする場合, SequenceEffectオブジェクトを利用すると, 複数のキーフレーム設定を直列化し単一のAnimationオブジェクトとして扱うことが可能になります.

SequenceEffectオブジェクトの動作サンプル

効果的なアニメーションの利用

アニメーションの採用基準

アニメーションを駆使したWEBページは見ていて楽しい反面, その実行にはコンピュータ資源の消費という対価を払う必要があります. つまり不用意なアニメーションは必要以上にメモリを消費し, CPUを発熱させることで消費電力を増大させます. これはバッテリ駆動を前提としたスマートフォンやタブレット環境において端末の稼働時間が短縮することにつながります. また, ユーザーの目はアニメーションの動きに敏感に反応します. そのため下手なアニメーションは本来見せたいコンテンツへの意識を散漫とさせることで, 情報の伝達を阻害します(ユーザビリティー, アクセシビリティーの低下).

以上のことから, WEBページ上にアニメーション機構を付ける場合は次の点を考慮します.

不必要なアニメーションを省く

アニメーションは人間の目に映ってこそ意味があるため, 何らかの原因でスクリーンに表示されないものについては積極的にその動作を停止・中断すべきです. 以下にこの操作に応用可能なAPIについて示します.

Intersection Observerによるアニメーションの遅延開始

スクロールを伴うWEBページにおいて, アニメーション対象がスクリーンに表示されているかどうかを判定するには繁雑な座標取得処理が必要となります. この問題に対処するため現在Intersection Observer機構が提案されています.

以下はIntersectionObserverオブジェクトを使ってアニメーションの開始をスクリーン表示時まで遅延した例です.

Page Visibility APIを用いたアニメーションの中断・再開

先ほどと同様にWEBページが別タブの表示やウインドウの最小化に伴い非表示となった場合, アニメーションを継続する必要性は薄れます. そこで, WEBページの非表示となったタイミングをvisibilitychangeイベントで捕捉し, アニメーションの中断・再開を行うようにします.

低コストなアニメーションの実現

アニメーションを実現する際, 見た目は同じであってもその実装方法によっては動作負荷に大きな違いが出てきます. 負荷の高いアニメーションは単位時間あたりのスクリーン書き換え回数を減少させ, アニメーションの動作をぎこちなくすることでWEBページの印象を損ないます. 従って最適なアニメーションを作ることはWEBページへの好感を保つ上で重要であり, そのためにはWEBブラウザ内部の動作を意識する必要があります.

WEBブラウザはHTMLコードを次のフローに則ってスクリーンに表示します.

  1. HTMLコードの分析とDOMツリーへの展開
    HTMLコードの構造を分析し, その構造をツリー構造に変換する処理です.
  2. スタイルルールの生成
    上と並行してCSSコードを分析し, スタイルルールを算出する処理です.
  3. レンダーツリーの構築
    DOMツリーとスタイルルールとを組み合わせてDOMノードごとの描画順を算出する処理です.
  4. レイアウト処理
    DOMノード毎のスタイル値を求め, スクリーン上の座標等が決定されます.
  5. 描画処理
    レイアウトの内容を元に文字や色等を実際にスクリーンに描画します. スタイルの内容によっては描画内容がグラフィックメモリ内部で管理される事もあります.

これらの処理はユーザーによる操作やDOM・スタイルの書き換えにより逐一実行されますが, アニメーション処理において特に意識すべきものは最後の2つです. これらの暗黙の実行を如何に抑えるかが最適なアニメーション設計における急所と言えます.

再レイアウトを防ぐ

アニメーションに伴いDOMノードの位置やサイズが変化する場合, その影響範囲を局所化するように気をつけます. 例えばpositionプロパティをabsoluteやfixedに設定することでノードの位置を変化させても全体としての再レイアウトは起こりません. また, transformプロパティによる移動・変形はノードのレイアウトはそのままにスクリーンへの描画のみを変化させる効果を持つため, アニメーション対象として最適です.

レイアウト値の再計算を防ぐ

特定のAPI操作は意図しないレイアウト処理を引き起こします. getComputedStyleで得たスタイルオブジェクトの操作, getClientRectsメソッドの実行, offsetTopプロパティへのアクセスがこれにあたり, API呼び出し時点でのレイアウト値算出を試みるからです. 従って出来る限りこれらの呼び出し回数を減らすように設計します.

再描画に伴うコストを下げる

アニメーションとスクリーンの再描画は切っても切り離せない関係にあり, 普通に考えると対処のしようがないように思われます. が, もともとコストの高いプロパティ(box-shadow, text-shadow, filter等)を伴うノードはアニメーション化させない, もしくはそもそもPNG画像としておいてから動かすと言った工夫を行うことで無駄な処理を省くことが可能です. 同様に固定グラフィック部を広め, 実際にアニメーションする範囲を狭めると言った工夫も効果的です.

錯覚・残像を利用する

テレビ映像等の連続画像が人間の目に臨場感に溢れて映るのは, それを見た人の脳がフレーム間の映像を経験に基づき補間しているからです. 従ってブラウザ上でのアニメーションにおいても, 一般的なテレビアニメーションでのコマ数(秒間24コマ)程度まで描画回数を落としたとしてもさほど気にならないはずです. もちろんアニメーション変位が大きい場合や小さい場合は描画頻度を増減する調整は必要です.

とは言え, ブラウザ毎に得意な処理が異なるため全てのケースを網羅する最適解は存在しません. そのためある種の直感に頼らざるを得ない面もあります. たまにはWEBブラウザの視点に立ち, 処理を自作する上でこれは重いとかこれなら軽そうだと言った仮説を立て, 実際にパフォーマンスを計測すると良いトレーニングとなるでしょう.

参考文献