なめらかなウェブアニメーションのための10の原則
CSSで60fpsのアニメーションを実現する、完全ガイド

昨年Gyroscopeを出してから、アニメーションに使っているJavaScriptのライブラリについて、たくさんの人に聞かれました。公開しようかとも考えましたが、実のところ、魔法はそこで起きているわけではありません。
特別なJavaScriptのプラグインがこうした問題を魔法のように解いてくれる、そんなふうに思ってほしくないのです。私たちがやっているのは、ほとんどが、最近のブラウザの性能、GPU、そしてCSS3の仕様の進歩を利用することだけです。
すばらしいアニメーションに銀の弾丸はありません。時間をかけて試し、詰めていくしかない。それでも、何年も実験を重ねてブラウザ性能の限界にぶつかってきた中で、まず間違いなく気持ちのいいアニメーションになる、デザインとコードの原則がいくつか見えてきました。この手法を使えば、なめらかに感じられて、最近のデスクトップとモバイルのブラウザで動き、そして何より、あとから手を入れやすいページになります。

技術も実装も、人によって少しずつ違うでしょう。それでも、こうした大まかな原則はほとんどの場面で役に立つはずです。
アニメーションとは何でしょう
アニメーションはインターネットより前からあり、うまくつくれるようになるには一生かけて学べるほどの奥行きがあります。ただ、インターネットでやるとなると、独特の制約と難しさがあります。
なめらかな60fpsを出すには、1フレームを16ms以内に描き終える必要があります。まったく余裕がありませんから、1フレームごとにとても効率のいい描き方を見つけなければなりません。
ウェブでアニメーションを実現する方法は、何十通りもあります。たとえばフィルムストリップは、インターネットより前からあるやり方です。少しずつ違う手描きのコマを1秒に何度も差し替えて、動いて見せる錯覚をつくります。
Twitterは最近、新しいハートのアニメーションでこの単純なやり方を使い、コマを並べたスプライトをめくっていきました。

この効果は、小さな要素を大量に個別に動かしてもつくれたでしょうし、SVGでもできたかもしれません。でも、それでは無駄に複雑ですし、たぶんここまでなめらかにはなりません。

多くの場面では、要素が変化するときにCSSのtransitionプロパティで自動的に動かしたくなるはずです。この手法は「tweening」とも呼ばれます。ふたつの異なる値のあいだを移っていく、という意味です。この方法の良さは、そのためのロジックを自分で組まなくても、簡単に中断したり逆再生したりできることです。イントロのような「仕掛けて忘れる」たぐいのアニメーションや、ホバーのような単純なやり取りにぴったりです。
さらに読む: CSS Transitionsについて知っておくべきこと

別の場面では、キーフレームを使うCSSのanimationプロパティが、ずっと動き続ける背景の細部に向いていることもあります。たとえばGyroscopeのロゴのリングは、いつも回り続けるように仕込まれています。ほかにCSSのanimation構文が生きるのは、歯車の回転比のようなものです。
前置きはこのくらいにして、アニメーションの性能を大きく良くしてくれるはずのコツを紹介します…
#1
opacityとtransform以外のプロパティは変えないこと。
大丈夫そうに思えても、やめておきましょう。
この基本の原則ひとつで、モバイルでも8割がたたどり着けます。どこかで聞いたことがあるはずです。私が思いついた話ではありませんし、そのわりに守られていません。ウェブ版の「健康的に食べて運動しよう」で、いい助言に聞こえるのにたぶん無視されている、あれです。
その考え方に慣れてしまえばとても分かりやすいのですが、昔ながらのCSSプロパティを動かすのに慣れている人には、大きな飛躍かもしれません。
たとえば、何かを小さくしたいなら、widthを変えるかわりにtransform: scale()が使えます。動かしたいなら、marginやpaddingをいじって毎フレームでページ全体のレイアウトを組み直させるのではなく、単純なtransform: translateXやtransform: translateYで済みます。
なぜこれで効くのでしょう
人間から見ると、widthやmarginなどを変えるのは大したことに思えないかもしれません。単純なぶん、そちらのほうがいいとさえ思えます。でも、コンピュータがやらされる仕事という点では両者は別世界で、片方はずっとずっとひどいのです。
ブラウザのチームは、こうした処理の最適化にすばらしい仕事を積み重ねてきました。transformは効率よく処理しやすく、要素を描き直さずにグラフィックカードの力を借りられることも多いのです。
ページの最初の読み込みでは、思いきりやってかまいません。角を全部丸めて、画像を使って、あちこちに影を落として、よほど無鉄砲な気分なら動的なぼかしだってやればいい。一度きりなら、計算に数ミリ秒余分にかかっても問題ありません。ただ、いったん描き終わったあとは、全部を計算し直し続けたくはありません。
さらに読む: translateで要素を動かす(Paul Irish)
#2
見えているのに隠す。
要素を隠すには、opacityをなくすのと一緒にpointer-events: noneを使う
これはブラウザによって注意点があるかもしれませんが、webkitやほかの最近のブラウザ向けにつくっているだけなら、ずいぶん楽になります。
ずっと昔、アニメーションをjQueryのanimate()で扱わなければならなかったころ、フェードイン、フェードアウトの複雑さの多くは、display: noneとblockをちょうどいいタイミングで切り替えるところから来ていました。早すぎればアニメーションが終わらず、遅すぎればopacityゼロの見えない中身がページを覆ってしまう。アニメーションが終わったあとの後片づけには、いちいちコールバックが必要でした。
CSSのpointer-eventsプロパティは(かなり前からあるのに、あまり使われていません)、要するにその要素をクリックにも操作にも反応させなくします。まるでそこに無いかのように。CSSで簡単にオンオフでき、アニメーションを邪魔することも、描画や表示に影響を与えることもありません。
opacityゼロと組み合わせれば、実質的にdisplay: noneと同じ効果になります。しかも、新しい描画を引き起こす性能上の代償がありません。何かを隠すときは、たいていopacityを0にしてpointer-eventsを切るだけで、あとはその要素のことは忘れてしまえます。勝手に面倒を見てくれるとわかっているので。
これは絶対配置の要素と特によく合います。ページのほかの部分に一切影響していないと、安心して言えるからです。
それに、少し余裕も生まれます。タイミングが完璧でなくてもいいのです。要素が見えていた時間より1秒長くクリックできたり、ほかのものを覆っていたりしても、あるいは完全にフェードインしてからやっとクリックできるようになっても、世界は終わりません。
#3
全部を同時に動かさないこと。
そのかわり、振り付けを。
ひとつのアニメーションは単体ならなめらかでも、ほかのたくさんと同時に走れば、たぶん台無しになります。ほとんど何でも、なめらかに動く簡単なデモをつくるのは簡単です。でも、サイト全体でその性能を保つのは、桁違いに難しい。だからこそ、きちんと段取りを組むことが大事です。
すべてがまったく同じ瞬間に始まったり走ったりしないよう、タイミングをばらけさせたくなるはずです。だいたい2つか3つまでなら、特に始まりが少しずつずれていれば、同時に動いても遅くなりません。それ以上になると、かくつきの危険が出てきます。
ページに文字どおりひとつしか物がないのでもないかぎり、振り付けという考え方を理解することが大事です。ダンスの言葉に見えるかもしれませんが、インターフェースを動かすうえでも同じくらい大事です。物は正しい方向から、正しいタイミングで入ってこなければなりません。ばらばらの存在であっても、よく設計されたひとつのまとまりの一部だと感じられるべきです。
この話題については、GoogleのMaterial Designにおもしろい提案がいくつかあります。それだけが正解というわけではありませんが、考えて試してみる価値はあります。

さらに読む: Google Material Design · Motion
#4
transitionのdelayを少しずつ増やすと、動きの振り付けが簡単になります。
アニメーションの振り付けは本当に大事で、しっくりくるまでにはたくさんの実験とテストが要ります。それでも、そのためのコードはそれほど複雑である必要はありません。
私はたいてい、親要素(多くの場合はbody)のクラスをひとつ切り替えて、たくさんのtransitionを引き金にします。それぞれに違うtransition-delayを持たせて、ちょうどいい時間に入ってくるようにするのです。コードの側から見れば、気にするのは状態の変化ひとつだけ。JavaScriptの中に何十ものタイミングを抱えずに済みます。

一連の要素を少しずつずらすのは、振り付けの簡単でわかりやすいやり方です。これが強いのは、見た目が良くなると同時に、貴重な性能も稼いでくれるからです。同時に動かすのは数個だけにしたい、という話を思い出してください。ひとつずつがなめらかに感じられる程度には広げつつ、全体が遅く感じられるほど広げすぎないように。個別の物が鎖のように続くのではなく、途切れない流れに感じられるくらいには重ねたいところです。
コード例
要素をずらすには、いくつか簡単な手があります。特に長いリストのときに効きます。項目が10個より少ないときや、静的なページのように数がとても読めるときは、私はたいていCSSに値を書きます。これがいちばん単純で、手入れも楽です。

もっと長いリストや、動きの激しい中身なら、各項目をループしてタイミングを動的に設定できます。

だいたい変数はふたつです。基準になるdelayと、項目どうしの間隔。ちょうどいいところを見つけるのは難しいのですが、数字がぴたりと合ったときは、本当に完璧に感じられます。
#5
全体にかかる倍率を用意して、スローモーションで設計する
そのあとで、全部を速くします。
アニメーションの設計では、タイミングがすべてです。仕事の20%は何かを実装すること、残りの80%は、全部をそろえてなめらかに感じさせる正しいパラメータと長さを見つけることです。
特に複数の要素の振り付けをしていて、ページから性能と同時実行を絞り出そうとしているときは、全体をスローモーションで見られると格段に楽になります。
JavaScriptでも、SASSのようなCSSのプリプロセッサ(私たちは大好きです)でも、ちょっとした計算を足して変数で組み立てるのは、それほど難しくないはずです。
違う速さやタイミングを手軽に試せるようにしておきましょう。たとえば1/10の速さでもかくつくなら、根本的に何かがおかしいのかもしれません。50倍に引き伸ばしてなめらかなら、あとは走れるいちばん速い速度を見つけるだけの話です。等速では5ミリ秒の乱れに気づきにくくても、全体を遅くすれば嫌でも目につきます。
特にとても複雑なアニメーションや、やっかいな性能のボトルネックを解くときには、スローモーションで見られることが本当に役立ちます。
考え方の中心は、遅く動かしているあいだに細部を完璧に詰め込んでおいて、そのあと全体を速くして完璧に感じさせる、というものです。とても微妙な違いですが、ユーザーはそのなめらかさと細部に気づきます。
この機能は、実はOS Xにもあります。しまうボタンやアプリのアイコンをshiftを押しながらクリックすると、スローモーションで動くのが見えます。Gyroscopeでも、一時期このスローモーションをshiftキーで発動できるように実装していました。
#6
UIを動画に撮って見返すと、第三者の目という貴重な視点が手に入ります。
違う視点に立つと、ものごとがはっきり見えることがあります。動画はそのためのいい方法です。
After Effectsで動画をつくって、それをサイトで実装しようとする人もいます。私はむしろ逆をやることが多く、サイトのUIから良い動画をつくろうとします。
何かをVine*や動画で出せるというのは、かなり高い基準です。ある日、つくったものがうれしくて、友人に見せようと録画してみました。
ところが、見返してみると、良くないところがたくさん目につきました。大きなかくつきがあって、タイミングもどれも少しずつずれている。少し気恥ずかしくなって、送るのはやめて、まだやることがたくさんあると気づきました。
実際に触っているあいだは、こういうものは見過ごしがちです。でも動画で、何度も繰り返して、あるいはスローモーションでアニメーションを見ると、問題は嫌というほどはっきりします。
カメラは10ポンド太って見せる、と言います。たぶん10フレームも足してくるのでしょう。
いまでは、自分のページのスローモーション動画を見て、どこか気に入らないコマがあれば直すことが、仕事の大事な一部になりました。ブラウザが遅いせいだと片づけるのは簡単ですが、もう少し最適化してテストすれば、そういう問題はたいてい越えられます。
動画にかくつきが写っても恥ずかしくなくなって、これなら人に見せられると思えたら、そのページはたぶんリリースの準備ができています。
#7
ネットワークの通信は、かくつきの原因になります。
大きなHTTPリクエストは先読みするか、あとに回しましょう
これの犯人としては画像が大きいです。大きなものが数枚(背景の大画像など)でも、小さなものが大量(アバターが50個読み込まれる場面を想像してください)でも、単に中身が多い(フッターまで画像が続く長いページ)でも同じです。
ページを最初に読み込むときは、大量のものが初期化されてダウンロードされています。アナリティクスや広告、そのほかのサードパーティのスクリプトがあると、さらにひどくなります。読み込みのあと、アニメーションを全部ほんの数百ミリ秒だけ遅らせるだけで、性能が見違えることもあります。
必要になるまでは、ここを過剰に最適化しないほうがいいですが、複雑なページをなめらかに動かすには、中身の読み込みにとても細かい遅延とタイミングが要ることもあります。基本としては、最初はできるだけ少ないデータだけを読み込み、重い処理とイントロのアニメーションが終わってから、ページの残りを読み込み続けるようにしたいところです。
データの多いページでは、全部を読み込む仕事はかなりの量になります。静的な中身ならうまく動くアニメーションも、同時に本物のデータを読み込み始めたとたん崩れることがあります。動くはずなのにおかしいときや、なめらかなときとそうでないときがあるなら、同時にほかのことをやっていないか、ネットワークの動きを確かめてみることをおすすめします。
#8
スクロールに直接ひもづけないこと。
格好いい考えに見えますが、実際はあまり良くありません。
スクロールに連動したアニメーションは、この数年で人気を集めることがあります。特にパララックスやその他の特殊効果を使ったものです。それが良いデザインかどうかは議論の余地がありますが、技術的な実装には、うまいやり方とまずいやり方があります。
この手のことをそこそこの性能でやる方法は、あるスクロール量に届いたことをイベントとして扱い、一度だけ発火させることです。自分が何をしているかよくわかっているのでなければ、この分野は避けることをおすすめします。簡単に転びますし、あとから直すのが本当に大変です。
もっとひどいのは、標準のスクロールバーを使わずに自分でスクロールの仕組みをつくること、つまりスクロールジャックです。どうかやめてください。
これはモバイルで特に効く決まりのひとつですが、理想のユーザー体験のためにも、たぶん守るべき習慣です。
もしスクロールや特殊なイベントを軸にした体験をどうしてもつくりたいなら、設計に時間をかける前に、性能が出せるかどうかを確かめる簡単な試作をつくることをおすすめします。
#9
モバイルでのテストは、早く、何度も。
ほとんどのウェブサイトはパソコンでつくられ、たいていはつくったのと同じマシンでいちばん多くテストされます。だからモバイルの体験とアニメーションの性能は、後回しになりがちです。技術によっては(canvasなど)、あるいは手法によっては、モバイルで同じようには動かないこともあります。
けれども、きちんと書いて最適化すれば(原則#1を参照)、モバイルの体験はパソコンよりなめらかにもなり得ます。モバイルの最適化はかつてとてもやっかいな話でしたが、いまや新しいiPhoneはたいていのノートパソコンより速いのです。ここまでのコツを守ってきたなら、そのままでモバイルでも十分な性能が出ているかもしれません。

モバイルからの利用は、ほとんどのサイトで大きく、とても大事な部分になります。極端に聞こえるかもしれませんが、まる一週間、自分のスマートフォンからだけ見てみることをおすすめします。モバイル版を使わされることが罰のように感じられてはいけないのに、たいていはそう感じられてしまいます。
大きい版のサイトと同じくらい磨かれていて、同じくらい快適だと感じられるまで、デザインの改善と性能の改良を続けてください。
一週間、自分のモバイルサイトしか使わないと決めれば、たぶん大きい版よりも良い体験になるまで詰めることになります。使っていて苛立つのは、それでも報われます。ユーザーがぶつかる前に問題が直るのですから。
#10
いろいろな端末で、こまめにテストする
画面のサイズ、密度、端末。どれも大きく効いてきます
モバイルかデスクトップかのほかにも、性能を大きく左右する要素はたくさんあります。画面が「retina」かどうか、ウィンドウの総ピクセル数、ハードウェアがどれくらい古いか、などです。
ChromeとSafariはどちらもWebkit系で構文も似ていますが、それぞれに癖があります。Chromeは更新のたびに何かを直し、新しいバグを持ち込むこともありますから、いつも気を抜けません。
もちろん、いちばん低い水準に合わせてつくりたいわけではありませんから、装飾を段階的に足したり外したりする賢いやり方を見つけられると、とても役に立ちます。
私は小さなMacBook Airと大きなiMacを行き来していますが、そのたびに小さな問題と直すべきところが見つかります。特にアニメーションの性能について。それだけでなく、全体のデザイン、情報の密度、読みやすさなどについても。
メディアクエリは、こうした違いに対応するとても強い道具です。高さや幅でスタイルを変えるのがよくある使い方ですが、ピクセル密度やほかの性質で狙いを付けることもできます。OSや端末の種類を知るのも役に立ちます。モバイルの性能の特性は、パソコンとまるで違うことがあるからです。

ここで紹介した手法が、次のプロジェクトで役に立つことを願っています。うまくいきますように。


