web-animations-jsで策定中のAPIを先取りする


この記事は[JavaScript Advent Calendar 2014](http://qiita.com/advent-calendar/2014/javascript)の記事でもなければ、[Frontrend Advent Calendar 2014](http://qiita.com/advent-calendar/2014/frontrend)の記事でもありません。
<small>べ、べつに書こうと思ったらすでに埋まってたとかじゃないんだからねっ！</small>

CSSアニメーションやSVGアニメーションなどを抽象的にタイムラインベースで扱うことのできる **web-animations-js** が便利なので使い方などを書いておきたいと思います。

- 公式サイト：https://www.polymer-project.org/platform/web-animations.html
- リポジトリ：https://github.com/web-animations/web-animations-js

## これは何？
web-animations-jsは、 __Web Animations API__ をJSで実装したPolyfillです。
[Polymer](https://www.polymer-project.org/)のサブプロジェクトとして開発されていますが、特に依存しているわけではないので単体で利用することができます。

動作ブラウザはPolymerの[Browser Compatibility](https://www.polymer-project.org/resources/compatibility.html)に準じているのでIEは10以上とされています。
一通り試してみたところ、IE9でもHTML要素のアニメーションだけであれば問題ないように見えました。

### Web Animations API とは
冒頭にも書いたWebページ上のアニメーションを抽象的に扱うための本APIは、GoogleとMozillaが中心となって策定を進めているWeb Animationsという規格によって定義されています。
2014年12月現在、 __1.0 Working Draft__ と __Editor's Draft__ がそれぞれ公開中です。

- [Web Animations 1.0 W3C Working Draft 5 June 2014](http://www.w3.org/TR/web-animations/)
- [Web Animations Editor’s Draft, 27 November 2014](http://w3c.github.io/web-animations/)

ブラウザのネイティブレベルでは、Chrome 36から __Element.animate__ がすでに実装されています。
参考：[Web Animations - element.animate() is now in Chrome 36](http://updates.html5rocks.com/2014/05/Web-Animations---element-animate-is-now-in-Chrome-36)
ちなみにChrome以外はこんな感じ→ http://caniuse.com/#feat=web-animation

### なぜ必要か
Web Animations APIを必要とする理由について[公式サイト](https://www.polymer-project.org/platform/web-animations.html)の冒頭では、アニメーションに関する仕様ならすでにWebには __CSS Transitions__, __CSS Animations__, __SVG Animations / SMIL__, __requestAnimationFrame()__ の四つがあるよね、とした上で次のように書かれています。

> - _CSS Transitions / CSS Animations are not very expressive_ - animations can’t be composed, or sequenced, or even reliably run in parallel; and animations can’t be tweaked from script.
> - _SVG Animations are very expressive, but also very complicated._ SVG Animations can’t be applied to HTML content.
> - _requestAnimationFrame() is not a declarative approach_ - it requires the use of the main thread, and will therefore jank if the main thread is busy.

- __CSS Transitions__ と __CSS Animations__ は順番に実行したり並列に実行したりを確実にはできないし、スクリプトから微調整したりもできなくて表現力に乏しいよね。
- __SVG Animations__ って表現力はすごいけどめちゃくちゃ複雑だよね。  
  <small>(以下の意味を掴みかねておりますボスケテ→ <q>SVG Animations can’t be applied to HTML content.</q>)</small>
- __requestAnimationFrame()__ はメインスレッドを使うからビジー状態だと辛くなるよね。

ざっくり意訳するとこんな感じでしょうか？
さらに、Web Animationsの規格としての目的を以下のように記しています。

> - The code cost of supporting animations on the web is reduced.
> - The various animations specifications are interoperable.
> - Spec authors and browser vendors have a single place to experiment with new animation innovations to improve the Web for the future.

- Web上のアニメーションを実現するためのコードコストを削減するよ。
- 様々なアニメーションの仕様の相互運用を可能にするよ。
- 未来のWebをより良くするために、仕様策定者とブラウザーベンダーがアニメーションのイノベーションを目指して一つの場所に集うよ。

と非常に野心的でフロントエンドの実装者にとってもメリットの大きな規格となっています。
まあPolymer本体やAngularでそうあるように、自分たちのブラウザに実装している（したい）機能を標準仕様に押し上げたいGoogleの姿勢がここでも見られる感じです。

### 機能別に用意された3つのバージョン
web-animations-jsはずっと単一ビルドのみで1年半以上前から存在していたのですが、先日のアップデートの際に利用できる機能が違う3つのビルドに分けられました。

- __web-animations.min.js__:  
  すでにブラウザにネイティブレベルで実装されている機能のみ使用可能。  
  現状ではChromeに搭載されているElement.animateとPlayback Controlを指す。
- __web-animations-next.min.js__:  
  web-animations.min.jsの機能に加えて、仕様として提案中の機能やネイティブでは未搭載の機能が利用可能。
- __web-animations-next-lite.min.js__:  
  web-animations-next.min.jsからあまり使われない機能を省いた軽量版。

それぞれに利用可能な機能は一覧表をご覧ください→ https://github.com/web-animations/web-animations-js#build-target-comparison

また、単一ビルド時代のレガシーバージョンはこちらにホスティングされています→ https://github.com/web-animations/web-animations-js-legacy

## 基本的な使い方
リポジトリのREADMEでは基本的な使い方として、要素単体をアニメーションさせる __Element.animate__ について説明しています。

なお、web-animations-jsは今のところnpmやbowerに公開がされていないので、git cloneするなりDownload ZIPするなりして適当に取得してください。

```HTML
<!-- web-animations-js を読み込む -->
<script src="web-animations.min.js"></script>

<!-- アニメーションさせたい要素 -->
<div class="pulse" style="width:150px;">Hello world!</div>

<!-- JS -->
<script>
  // アニメーションさせたい要素を取得する
  var elem = document.querySelector('.pulse');

  // Element.animateにアニメーションの内容を渡して実行する
  var player = elem.animate([
    {opacity: 0.5, transform: "scale(0.5)"},
    {opacity: 1.0, transform: "scale(1)"}
  ], {
    direction: 'alternate',
    duration: 500,
    iterations: Infinity
  });
</script>
```
DEMO: http://codepen.io/haribote/pen/pvjWzy

### Element.animateに渡すもの
Element.animateは2つの引数を受け取ります。

1. 第一引数 {Array}:  
   CSSプロパティを記述したオブジェクトをアニメーションさせたい順番に並べた配列。
1. 第二引数 {Object} or {Number}:  
   アニメーションのさせ方を記述したオブジェクト、もしくはアニメーションにかける時間（ミリ秒）を指定した数値。  
   アニメーションのさせ方についてはCSS Transitionsと同様に __duration__, __iterations__, __iterationStart__, __fill__, __delay__, __playbackRate__, __direction__, __easing__の指定が可能。  
   詳しくはこちら→ https://www.polymer-project.org/platform/web-animations.html#controlling-the-animation-timing

### Element.animateが返すもの
Element.animateを実行すると __AnimationPlayer__ というオブジェクトが返ってきます。
console.dirで見てみると以下のようなプロパティやメソッドが組み込まれています。

- __startTime__ {Number}:  
  AnimationPlayerの開始時間を示すプロパティ。
- __currentTime__ {Number}:  
  AnimationPlayerの到達時点を示すプロパティ。
- __playbackRate__ {Number}:  
  AnimationPlayerの再生速度を示すプロパティ。
- __onfinish__ {Function}:  
  再生完了時のコールバック。
- __playState__ _(readonly)_:  
  AnimationPlayerの再生状況を示すプロパティ。
- __cancel()__ _(prototype)_:  
  AnimationPlayerの全てのエフェクトをクリアする。  
  実行するとonfinishが呼ばれる。
- __finish()__ _(prototype)_:  
  AnimationPlayerを終了時点までスキップさせる。  
  実行するとonfinishが呼ばれる。
- __play()__ _(prototype)_:  
  AnimationPlayerを再生させる。  
- __pause()__ _(prototype)_:  
  AnimationPlayerを一時停止させる。  
- __reverse()__ _(prototype)_:  
  AnimationPlayerを逆転再生させる。  

### コールバック関数の登録
AnimationPlayerオブジェクトに用意されている中でも _onfinish_プロパティは特に重要です。
_onfinish_プロパティに関数を登録することでアニメーション終了時に任意の処理を実行することが可能です。

```HTML
<!-- web-animations-js を読み込む -->
<script src="web-animations.min.js"></script>

<!-- アニメーションさせたい要素 -->
<div class="pulse" style="width:150px;">Hello world!</div>

<!-- JS -->
<script>
  // アニメーションさせたい要素を取得する
  var elem = document.querySelector('.pulse');

  // Element.animateにアニメーションの内容を渡して実行する
  var player = elem.animate([
    {opacity: 0.5, transform: "scale(0.5)"},
    {opacity: 1.0, transform: "scale(1)"}
  ], {
    direction: 'alternate',
    duration: 1000
  });

  // コールバック関数を登録する
  player.onfinish = function() {
    window.alert('Finished!');
  };
</script>
```
DEMO: http://codepen.io/haribote/pen/LEpzyJ

なおiterationsにInfinityを指定した場合は、明示的に _cancel_メソッドを叩かない限り _onfinish_ は呼ばれません。

基本的な使い方として説明している機能についてはChrome 36以降であればネイティブに実装されているので _web-animations.min.js_ で利用が可能です。
またこの程度の単純なアニメーションであればjQueryの _animate_メソッドでも十分かもしれません。
続いては、複数の要素及びアニメーションを連動的に動作させる方法について説明します。

## 連動的な使い方
複数の要素及びアニメーションを連動的に動作させるには、規格として提案中の機能を使うために __web-animations-next.min.js__が必要です。

### アニメーションモデルを作成する
基本的な使い方ではElement.animateによってアニメーションを即時に再生させましたが、 web-animations-next.min.jsでは __Animationオブジェクト__を用いることでアニメーションモデルと再生のコントロールを分離することができます。

```HTML
<!-- web-animations-js を読み込む -->
<script src="web-animations-next.min.js"></script>

<!-- アニメーションさせたい要素 -->
<div class="pulse" style="width:150px;">Hello world!</div>

<!-- JS -->
<script>
  // アニメーションさせたい要素を取得する
  var elem = document.querySelector('.pulse');

  // Animationオブジェクトを生成する
  var myAnimation = new Animation(elem, [
    {opacity: 0.5, transform: "scale(0.5)"},
    {opacity: 1.0, transform: "scale(1)"}
  ], {
    direction: 'alternate',
    duration: 1000
  });

  // document.timeline.playにAnimationオブジェクトを渡して実行する
  var player = document.timeline.play(myAnimation);

  // コールバック関数を登録する
  player.onfinish = function() {
    window.alert('Finished!');
  };
</script>
```
DEMO: http://codepen.io/haribote/pen/empKgR

#### Animationオブジェクトコンストラクター
Animationオブジェクトのコンストラクターは3つの引数を受け取ります。
第一引数にはHTMLElementオブジェクトを渡してアニメーションをさせる要素を指定します。
第二・三引数は基本的な使い方で使用したElement.animateの第一・二引数と基本的に同じです。


Animationオブジェクトが持つプロパティやメソッドについてはconsole.dirなどで確認してみてください。

#### document.timeline.play
Animationオブジェクトをdocument.timeline.playに渡して実行することでアニメーションの再生が開始されます。
また、Element.animateと同じくAnimationPlayerオブジェクトが返されます。

### 同時にアニメーションさせる
複数の要素及びアニメーションを __同時__ に再生させるには __AnimationGroup__オブジェクトを用います。
使い方は、コンストラクターにAnimationオブジェクトを配列に集合させて渡すだけなので簡単です。

```HTML
<!-- web-animations-js を読み込む -->
<script src="web-animations-next.min.js"></script>

<!-- アニメーションさせたい要素 -->
<div id="elem1" style="width:150px;">foo</div>
<div id="elem2" style="width:150px;">bar</div>

<!-- JS -->
<script>
  // アニメーションさせたい要素を取得する
  var elem1 = document.querySelector('#elem1');
  var elem2 = document.querySelector('#elem2');

  // Animationオブジェクトの集合を配列にまとめる
  var animations = [
    new Animation(elem1, [{ transform: "translate(0, 0)" }, { transform: "translate(0, 64px)" }, { transform: "translate(190px, 64px)" }], { direction:'alternate', duration:1000, fill:'both', easing:'ease-out'}),
    new Animation(elem2, [{ transform: "translate(0, 0)" }, { transform: "translate(-190px, 0)" }, { transform: "translate(-190px, 64px)" }], { direction:'alternate', duration:1000, fill:'both', easing:'ease-out'})
  ];

  // AnimationGroupオブジェクトを生成する
  var myAnimationGroup = new AnimationGroup(animations);

  // document.timeline.playにAnimationGroupオブジェクトを渡して実行する
  var player = document.timeline.play(myAnimationGroup);

  // コールバック関数を登録する
  player.onfinish = function() {
    window.alert('Finished!');
  };
</script>
```
DEMO: http://codepen.io/haribote/pen/myejEV

### 順番にアニメーションさせる
複数の要素及びアニメーションを __順番__ に再生させるには __AnimationSequence__オブジェクトを用います。
こちらも、コンストラクターにAnimationオブジェクトを配列に集合させて渡すだけで使えます。

```HTML
<!-- web-animations-js を読み込む -->
<script src="web-animations-next.min.js"></script>

<!-- アニメーションさせたい要素 -->
<div id="elem1" style="width:150px;">foo</div>
<div id="elem2" style="width:150px;">bar</div>

<!-- JS -->
<script>
  // アニメーションさせたい要素を取得する
  var elem1 = document.querySelector('#elem1');
  var elem2 = document.querySelector('#elem2');

  // Animationオブジェクトの集合を配列にまとめる
  var animations = [
    new Animation(elem1, [{ transform: "translate(0, 0)" }, { transform: "translate(0, 64px)" }], { direction:'alternate', duration:600, fill:'both', easing:'ease-out'}),
    new Animation(elem1, [{ transform: "translate(0, 64px)" }, { transform: "translate(190px, 64px)" }], { direction:'alternate', duration:600, fill:'both', easing:'ease-out'}),
    new Animation(elem2, [{ transform: "translate(0, 0)" }, { transform: "translate(-190px, 0)" }], { direction:'alternate', duration:600, fill:'both', easing:'ease-out'}),
    new Animation(elem2, [{ transform: "translate(-190px, 0)" }, { transform: "translate(-190px, 64px)" }], { direction:'alternate', duration:600, fill:'both', easing:'ease-out'}),
    new Animation(elem1, [{ transform: "translate(190px, 64px)" }, { transform: "translate(190px, 0)" }], { direction:'alternate', duration:600, fill:'both', easing:'ease-out'}),
    new Animation(elem1, [{ transform: "translate(190px, 0)" }, { transform: "translate(0, 0)" }], { direction:'alternate', duration:600, fill:'both', easing:'ease-out'}),
    new Animation(elem2, [{ transform: "translate(-190px, 64px)" }, { transform: "translate(0, 64px)" }], { direction:'alternate', duration:600, fill:'both', easing:'ease-out'}),
    new Animation(elem2, [{ transform: "translate(0, 64px)" }, { transform: "translate(0, 0)" }], { direction:'alternate', duration:600, fill:'both', easing:'ease-out'})
  ];

  // AnimationSequenceオブジェクトを生成する
  var myAnimationSequence = new AnimationSequence(animations);

  // document.timeline.playにAnimationSequenceオブジェクトを渡して実行する
  var player = document.timeline.play(myAnimationSequence);

  // コールバック関数を登録する
  player.onfinish = function() {
    window.alert('Finished!');
  };
</script>
```
DEMO: http://codepen.io/haribote/pen/gbaKdb

### グループ・シーケンスの入れ子
AnimationGroupオブジェクトとAnimationSequenceオブジェクトはそれぞれに入れ子にすることが可能です。
組み合わせることでより複雑で連動的なアニメーションが実現できるわけです。

ソースが長いのでJSだけ貼っておきます。
なお。複数の要素に対して同じアニメーションを割り当てるのにunderscoreのmapメソッドを使っています。

```JavaScript
// アニメーションさせたい要素を取得する
var elBackground = document.querySelector('#background');
var elTitle = document.querySelector('#title');
var elHill1 = document.querySelector('#hill1');
var elHill2 = document.querySelector('#hill2');
var elHill3 = document.querySelector('#hill3');
var elsFlower = document.querySelectorAll('#flowers .flower');

var play = function() {
  // 入れ子になったAnimationオブジェクトの集合を作る
  var myAnimations = new AnimationSequence([
    new AnimationGroup([
      new Animation(elBackground, [{ transform:'translateY(0%)' }, { transform: 'translateY(-33%)' }], { duration:2000, fill:'both', easing:'ease-out' }),
      new Animation(elHill1, [{ transform:'translateY(20%)' }, { transform: 'translateY(0%)' }], { duration:2500, fill:'both', easing:'ease-out' }),
      new Animation(elHill2, [{ transform:'translateY(33%)' }, { transform: 'translateY(0%)' }], { duration:3000, fill:'both', easing:'ease-out' }),
      new Animation(elHill3, [{ transform:'translateY(33%)' }, { transform: 'translateY(0%)' }], { duration:3000, fill:'both', easing:'ease-out' })
    ]),
    new AnimationSequence(_.map(elsFlower, function(elFlower) {
      // 要素集合をmapメソッドで回してアニメーションオブジェクトを返す
      return new Animation(elFlower, [{ opacity: 0, transform:'scale(.1, .1) rotate(0deg) translateY(50px)' }, { opacity: 1, transform: 'scale(1, 1) rotate(-45deg) translateY(0px)' }], { delay:-300, duration:600, fill:'both', easing:'ease' })
    })),
    new Animation(elTitle, [{ opacity:0 }, { opacity:1 }], { duration:400, fill: 'both' }),
    new Animation(elTitle, [{ transform:'translate3d(0,0,0)', offset:0 }, { transform:'translate3d(0,0,0)', offset:0.2 }, { transform:'translate3d(0,-30px,0)', offset:0.4 }, { transform:'translate3d(0,-30px,0)', offset:0.43 }, { transform:'translate3d(0,0,0)', offset:0.53 }, { transform:'translate3d(0,-15px,0)', offset:0.7 }, { transform:'translate3d(0,0,0)', offset:0.8 }, { transform:'translate3d(0,-15px,0)', offset:0.9 }, { transform:'translate3d(0,0,0)', offset:1 }], {duration: 900, fill: 'both', easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)'})
  ]);

  // document.timeline.playにAnimationGroupオブジェクトを渡して実行する
  var player = document.timeline.play(myAnimations);

  // コールバック関数を登録する
  player.onfinish = function() {
    if (window.confirm('Finished! Play again?')) {
      play();
    }
  };

  // document.timeline.playはAnimationPlayerオブジェクトを返す
  console.dir(player);
};

play();
```

DEMO: http://codepen.io/haribote/pen/WbQKRZ

### グループ・シーケンスの便利なプロパティ・メソッド

AnimationGroupオブジェクトとAnimationSequenceオブジェクトには以下のような便利なプロパティやメソッドがあります。

- __activeDuration__:  
  登録された全てアニメーションの再生に必要な時間を示すプロパティ。  
  AnimationGroupオブジェクトでは登録されているAnimationオブジェクトの中の最大値。  
  AnimationSequenceオブジェクトでは登録されているAnimationオブジェクトの全合計。
- __prepend()__ / __append()__:  
  ~~AnimationGroupオブジェクトに組み込まれているメソッド。~~  
  ~~AnimationGroupオブジェクトの先頭(preoend)か最後(append)にAnimationオブジェクトを追加することができる。~~  
  ……はずなんだけど、現在のバージョンではundefinedです。仕様にも書かれているし、レガシー版では使えたのに！

<small>その他に、グループなのかシーケンスなのかを示す _type_プロパティというのもあったんですけどなくなってしまいました。 動的にアニメーションを組み合わせていくときに _prepend/append_と合わせて使うと便利だったんですが残念。</small>

## SVGを使ったモーションパスエフェクト
SVGを利用したモーションパスエフェクトは残念ながら現在のバージョンでは利用することができません。
リポジトリのREADMEには今後サポートしたい機能の一つに挙げられているものの、[最新のEditor’s Draft](http://w3c.github.io/web-animations/)ではWeb Animationsの規格から外されており、将来的に本当に実装されるのかちょっと疑問です。

また現在、[Motion Path Module Level 1](http://dev.w3.org/fxtf/motion-1/)がAdobeによって提案されており、Web AnimationsのEditor’s DraftにはMotion Path Moduleでやろうぜー、って書かれています。
もともとWeb Animations APIはGoogle/Mozillaに加えてAdobeも規格策定に携わっていたようですが、現在のEditorには一覧されていません。
AdobeはMotion Path Module規格の策定に集中することになったのでしょうか？
このあたりの詳しい経緯をご存じの方がいらっしゃいましたら是非教えてください！

一応どんな機能だったのかを書いておきます。
レガシー版では __MotionPathEffect__オブジェクトを用いることでSVGで書かれたパスをアニメーションの動線として定義することが可能でした。
MotionPathEffectオブジェクトをAnimationオブジェクトコンストラクターに渡すことで、SVGパスに沿わせて要素を動かすことができるわけです。

```html
<div class="disc">
  <p>foo</p>
</div>

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <defs>
    <path id="wheel" d="M 100,100 a 75,75 0 1,0 150,0 a 75,75 0 1,0 -150,0"></path>
  </defs>
</svg>

<script>
  // アニメーションさせたい要素を取得する
  var elem = document.querySelector('.disc');
  
  // MotionPathEffectオブジェクトを作成する
  var myEffect = new MotionPathEffect(document.querySelector('#wheel').pathSegList);
  
  // Animationオブジェクトコンストラクターの第二引数にMotionPathEffectオブジェクトを渡す
  var myAnimations = new AnimationGroup([new Animation(elem, myEffect, {
    duration: 3000,
    iterations: Infinity
  })]);

  // document.timeline.playにAnimationGroupオブジェクトを渡して実行する
  var player = document.timeline.play(myAnimations);
</script>
```

DEMO: http://codepen.io/haribote/pen/yyYrXm
<small>※ _web-animations-js-legacy_を使っています</small>。

## タイムラインの制御
Web Animations APIはアニメーション及びその集合に対してタイムラインベースでの制御を可能とします。
JSから再生状況に対してアクセスできるので、ユーザーアクションなどとの同期が比較的簡単に実現できます。

```JavaScript
// アニメーションを司るオブジェクト
var animation = {
  elms: {
    bg: document.querySelector('#background'),
    ttl: document.querySelector('#title'),
    hill1: document.querySelector('#hill1'),
    hill2: document.querySelector('#hill2'),
    hill3: document.querySelector('#hill3'),
    flowers: document.querySelectorAll('#flowers .flower')
  },
  effects: null,
  createEffects: function() {
    this.effects = new AnimationSequence([
      new AnimationGroup([
        new Animation(this.elms.bg, [{ transform:'translateY(0%)' }, { transform: 'translateY(-33%)' }], { duration:2000, fill:'both', easing:'ease-out' }),
        new Animation(this.elms.hill1, [{ transform:'translateY(20%)' }, { transform: 'translateY(0%)' }], { duration:2500, fill:'both', easing:'ease-out' }),
        new Animation(this.elms.hill2, [{ transform:'translateY(33%)' }, { transform: 'translateY(0%)' }], { duration:3000, fill:'both', easing:'ease-out' }),
        new Animation(this.elms.hill3, [{ transform:'translateY(33%)' }, { transform: 'translateY(0%)' }], { duration:3000, fill:'both', easing:'ease-out' })
      ]),
      new AnimationSequence(_.map(this.elms.flowers, function(elFlower) {
        // 要素集合をmapメソッドで回してアニメーションオブジェクトを返す
        return new Animation(elFlower, [{ opacity: 0, transform:'scale(.1, .1) rotate(0deg) translateY(50px)' }, { opacity: 1, transform: 'scale(1, 1) rotate(-45deg) translateY(0px)' }], { delay:-300, duration:600, fill:'both', easing:'ease' })
      })),
      new Animation(this.elms.ttl, [{ opacity:0 }, { opacity:1 }], { duration:400, fill: 'both' }),
      new Animation(this.elms.ttl, [{ transform:'translate3d(0,0,0)', offset:0 }, { transform:'translate3d(0,0,0)', offset:0.2 }, { transform:'translate3d(0,-30px,0)', offset:0.4 }, { transform:'translate3d(0,-30px,0)', offset:0.43 }, { transform:'translate3d(0,0,0)', offset:0.53 }, { transform:'translate3d(0,-15px,0)', offset:0.7 }, { transform:'translate3d(0,0,0)', offset:0.8 }, { transform:'translate3d(0,-15px,0)', offset:0.9 }, { transform:'translate3d(0,0,0)', offset:1 }], {duration: 900, fill: 'both', easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)'})
    ]);
  },
  player: null,
  playbackRate: 1.0,
  play: function() {
    if (this.effects === null) {
      this.createEffects();
    }

    if (this.player && this.player.playState === 'paused') {
      this.player.play();
    } else {
      this.player = document.timeline.play(this.effects);
    }
  },
  pause: function() {
    if (!this.player) {
      return;
    }

    // ポーズなのにストップなのでcurrentTimeをキャッシュしてあとから突っ込む
    var cuurentTime = this.player.currentTime;
    this.player.pause();
    this.player.currentTime = cuurentTime;
  },
  seek: function(val) {
    var _val = val / 100;
    if (!this.player) {
      this.play();
      this.pause();
    }
    this.player.currentTime = this.player.source.activeDuration * _val;
  }
};

// アニメーションを再生する
animation.play();

// コントローラーに機能を割り当てる
var elController = document.querySelector('#controller');
var elSeek = document.querySelector('#seek');

// Play/Pause
elController.addEventListener('click', function(ev) {
  ev.preventDefault();

  var target = ev.target;

  if (target.name === 'play' ) {
    if (animation.player && animation.player.playState === 'running') {
      animation.pause();
    } else {
      animation.play();
    }
  }
}, false);

// Seek
elSeek.addEventListener('mousedown', function(ev) {
  animation.pause();

  elSeek.addEventListener('mousemove', function(ev) {
    var target = ev.target;

    if (target.name === 'time') {
      animation.seek(parseInt(target.value, 10));
    }
  }, false);
}, false);
elController.addEventListener('mouseup', function(ev) {
  elSeek.removeEventListener('mousemove');
}, false);
window.setInterval(function() {
  if (animation.player && animation.player.playState === 'running') {
    elSeek.value = Math.round(animation.player.currentTime / animation.player.source.activeDuration * 100);
  }
}, 1000/60);
```
DEMO: http://codepen.io/haribote/pen/jEWOwY

## まとめ
近頃はちょっとしたページでもアニメーションをさせるのがトレンドのようで、受託の制作屋としてはそういったニーズに効率よく対応していかなくてはいけないわけですが、Web Animations APIを利用することで制作コストを下げることができれば良いなと思います。
個々のアニメーションモデルやタイムラインにアクセスすることのできる本APIはFlashに馴染みのある方などにも親しみやすく、単純にコストを下げるだけではなく表現の可能性を大きく広げるものではないでしょうか。

同じような目的を達成することのできるJSライブラリとしては[TweenMax](http://greensock.com/tweenmax)が有名で、現状ではweb-animations-jsよりも高機能で利用者も多いことでしょう。

web-animations-jsはWeb Animations APIのPolyfillであるとは言え未実装の機能もあります。
しかし標準規格を目指している仕様について、注目しておいて損はないんじゃないかと期待するのです。

最後に私のダサいデモだといまいち魅力が伝わらないかもしれないので公式のデモページをリンクしておきます！
http://web-animations.github.io/web-animations-demos