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

  • 64
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

この記事はJavaScript Advent Calendar 2014の記事でもなければ、Frontrend Advent Calendar 2014の記事でもありません。
べ、べつに書こうと思ったらすでに埋まってたとかじゃないんだからねっ!

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

これは何?

web-animations-jsは、 Web Animations API をJSで実装したPolyfillです。
Polymerのサブプロジェクトとして開発されていますが、特に依存しているわけではないので単体で利用することができます。

動作ブラウザはPolymerのBrowser Compatibilityに準じているのでIEは10以上とされています。
一通り試してみたところ、IE9でもHTML要素のアニメーションだけであれば問題ないように見えました。

Web Animations API とは

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

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

なぜ必要か

Web Animations APIを必要とする理由について公式サイトの冒頭では、アニメーションに関する仕様ならすでに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 TransitionsCSS Animations は順番に実行したり並列に実行したりを確実にはできないし、スクリプトから微調整したりもできなくて表現力に乏しいよね。
  • SVG Animations って表現力はすごいけどめちゃくちゃ複雑だよね。
    (以下の意味を掴みかねておりますボスケテ→ SVG Animations can’t be applied to HTML content.)
  • 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するなりして適当に取得してください。

<!-- 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プロパティを記述したオブジェクトをアニメーションさせたい順番に並べた配列。
  2. 第二引数 {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プロパティに関数を登録することでアニメーション終了時に任意の処理を実行することが可能です。

<!-- 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オブジェクトを用いることでアニメーションモデルと再生のコントロールを分離することができます。

<!-- 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オブジェクトを配列に集合させて渡すだけなので簡単です。

<!-- 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オブジェクトを配列に集合させて渡すだけで使えます。

<!-- 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メソッドを使っています。

// アニメーションさせたい要素を取得する
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です。仕様にも書かれているし、レガシー版では使えたのに!

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

SVGを使ったモーションパスエフェクト

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

また現在、Motion Path Module Level 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パスに沿わせて要素を動かすことができるわけです。

<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
web-animations-js-legacyを使っています。

タイムラインの制御

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

// アニメーションを司るオブジェクト
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が有名で、現状ではweb-animations-jsよりも高機能で利用者も多いことでしょう。

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

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