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 使うことになると思う。
簡単な使い方
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)
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 の文字列
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) で表現可能。
離散的ではない値の表現には 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 に関する部分をちゃんと読まないといけなさそう。
参考
これを上から下まで全部読むだけ