Edited at

Web Animations API を使ってみる

More than 1 year has passed since last update.

Animation周りが苦手だったので、Flash のタイムラインアニメーションエディタみたいなものを練習がてら作ってみたい、ということで勉強した。

本記事は勉強ログ気味。


Web Animations API とは

CSS Animation の keyframe 制御を JS から可能にしたようなもの。

CSS Animation は 自分で止まったり再開したりできないし、フレーム制御も出来ない。 JS から制御できないのでコントロールしづらかったが、その問題を解決する。


Web Animations 実装具合と Polyfill

Can I Use 見る限りは Firefox で 実装済み、 Chrome で実装中

https://caniuse.com/#feat=web-animation

polyfill なしで試した結果、chrome で elem.animate(...) は動くけど、 elem.getAnimations()document.timeline が存在しない感じだった。

実際は polyfill 使うことになると思う。

https://github.com/web-animations/web-animations-js/tree/866e01726cf61ffeabad2baa7f6c10e3cadee138


簡単な使い方

polyfill を npm から適用

import 'web-animations-js/web-animations-next.min.js'

使う

const el = document.querySelector('.my-selector')

el.animate([{opacity: 0}, {opacity: 1}], 1000)

1000ms かけてopacity: 0 から 1 まで遷移。デフォルトだとリピート無し。

無限リピート

const el = document.querySelector('.my-selector')

el.animate([{opacity: 0}, {opacity: 1}], {
duration: 1000,
iterations: Infinity
})

iterations で指定した回数繰り返す。

W3C の Spec 見る限りは次のコードも動くはずだが、 ポリフィルを使う限りは動かなかった。

const el = document.querySelector('.my-selector')

el.animate({opacity: 0}, 1000)

https://www.w3.org/TR/web-animations-1/#updating-the-finished-state


Stop & Go

const animation = el.animate([{opacity: 0}, {opacity: 1}], 1000)

animation.pause()
animation.play()

あるエレメントに適用されている全ての animation を操作する。

el.getAnimations().forEach(a => a.pause())

el.getAnimations().forEach(a => a.play())

ドキュメント内の全てのアニメーションを取得する。

const anims = document.timeline.getAnimations()


keyframe の offset 制御

el.animate(

[
{opacity: 0, offset: 0},
{opacity: 0.5, offset: 0.8},
{opacity: 1, offset: 1}
],
2000
)

0 から 始まり、 1 で終わる。 (0と1は省略可能)

この例では 2000 * 0.8 = 1600 ms かけて opacity: 0.5 になり、残り 400ms で 1になる。


easing 制御

el.animate(

[
{opacity: 0, offset: 0},
{opacity: 0.5, offset: 0.8, easing: 'ease-in-out'},
{opacity: 1, offset: 1}
],
2000
)

何も指定しない場合、 デフォルトの easing は linear。 ここで指定できるのは timing-function の文字列

https://developer.mozilla.org/ja/docs/Web/CSS/timing-function


CSS timing function

ある時間中に 0 から 1 まで値が変化(transisiton)する時、それがどのような変化を描くかの関数、を示す css 上での表現

組み込みの timinig-function は linear, ease, ease-in, ease-out, ease-in-out。

それらは 3次ベジェ曲線 cubic-bezier(x1, y1, x2, y2) で表現可能。

https://en.wikipedia.org/wiki/Bézier_curve#Cubic_B.C3.A9zier_curves

離散的ではない値の表現には steps, steps-start, step-end を使う。


React で使う

自分ならこういう風に表現するかなぁという実装

import 'web-animations-js/web-animations-next.min.js'

import React from 'react'
import ReactDOM from 'react-dom'

class Animation extends React.Component {
componentDidMount() {
const el = ReactDOM.findDOMNode(this)
const { timelines } = this.props
for (const tl of timelines) {
const { keyframes, ...others } = tl
el.animate(tl.keyframes, others)
}
}

render() {
return this.props.children
}
}

export default () => {
return (
<Animation
timelines={[
{
keyframes: [{ opacity: 1 }, { opacity: 0.5 }, { opacity: 1 }],
duration: 1000,
iterations: Infinity
},
{
keyframes: [
{ background: 'white', offset: 0 },
{ background: 'red', offset: 0.9 },
{ background: 'white', offset: 1 }
],
duration: 3000,
spacing: 'spaced(left)'
}
]}
>
<span>Hello</span>
</Animation>
)
}


不満

組み込みの easing 関数の任意フレームでの値を JS から知る方法がない。 getComputedProperty() を使うしかない。


次に調べること

普通に使うだけならこれだけで一通りなんでも出来る。

タイムラインアニメーションエディタを作る場合、要はこの keyframes の JSON を生成できれば良い。

任意の実行フレームに巻き戻したり、とかやるとややこしくなってくる。それはこれから調べる。

実行効率のために composition に関する部分をちゃんと読まないといけなさそう。


参考

https://www.w3.org/TR/web-animations-1/

これを上から下まで全部読むだけ