川田寛

モバイルWebのUIを速くする基本テクニックがわかる──Google I/O 2016 High Performance Web UI

こんにちは、ふろしきです!

私はHTML5 Experts.jpで、過去2年ほどGoogle I/Oの情報を発信し、Web技術の変化についてお伝えしてきました。振り返るとGoogleは、2014年にモバイルWebの提唱と技術要素の拡大を図り、2015年からは「RAIL(モバイルWebが目指すべきパフォーマンス指標)」や「Progressive Web Apps(アプリのように振る舞うWeb)」といった、モバイルとの親和性が高いWebを作り出すための”考え方”を推し進めました。今年2016年は、さらにそれを踏み込んでいったという感じがします。

今回のI/Oで取り上げるのもそのひとつ。毎度お馴染みGoogle Developer AdvocateのPaul Lewis氏による 「High performance web user interfaces」です。彼は、モバイルにおいて、時にアプリのように振る舞うことが求められる昨今のWeb、すなわち「Progressive Web Apps」について、UIで起こりがちなパフォーマンス問題と、その改善方法について紹介しています。

31

※ この講演、動画無しでは説明が難しかったり、前提知識も多かったりするので、私でかなりアレンジ・要約して紹介しています。より詳細に内容を知りたい場合は、ソースをみることをオススメします!

Webは時として、モバイルアプリのような体験が求められる

モバイルにおいて、ホームスクリーンは重要な場所だ。人々はホームスクリーンから、目的を達成するためのアプリを起動する。Webは、Add to Homescreenを使うことで、ホームスクリーンからWebサイトへアクセスすることができるようになった。

するとどうなるか。このホームスクリーンをよくみてほしい。どれがWebで、どれがネイティブアプリなのかは見分けがつかないだろう。Google Mapsなんかはネイティブにみえるけれど、他はまったく想像がつかない。しかしこれらが、Google Mapsと同様にネイティブアプリにみえるなら、Webはネイティブアプリのように振る舞うことが求められている。

スクリーンショット 2016-06-06 23.14.36

パフォーマンスモデル、インタラクションモデルの2つによって、Webはモバイルネイティブアプリのような振る舞いをえることができる。Progressive Web Appsを実現することができる。今日はこの2つのモデルのうち、パフォーマンスモデルの話をしたい。

昨年は、Paul IrishIlya Grigorikなどの私のチームのメンバーが、「RAIL」というパフォーマンスモデルについて話した。RAILとは、Responseは0.1秒、Animationは16ミリ秒、Idleは50ミリ秒、Loadは1秒で動作すべきというもの。ただ、それを聞いた人々は、たまに勘違いをする。この4つの要素は、どれも全て、最も重要なこととして語ってしまうのだ。それは間違っている。

例えば、Webサイトにおいて、タップした時に求められるのは、4つの要素のうちLoadが重要になる。Idleが重要になることはそこまでない。そして、ホームスクリーンからタップして起動されるProgressive Web Appsでは、ResponseやAnimationが重要になる。Webサイトをつくるのと、Progressive Web Appsをつくるのでは、求められることが違う。

スクリーンショット 2016-06-11 17.19.02

さて、このようにパフォーマンス面で求められることが異なるProgressive Web Apps。そこに、3つのコンポーネントがある。Side Navigation、Swipeable Cards、Expand an Collapse。これらを実現するセオリーを紹介しよう。

1. Side Navigation

スクリーンショット 2016-07-03 22.27.14

まずは、このコンポーネント。メニューボタンをタップすると左からスライドインするバー。これは、2つのElementによって構成される。半透明の黒い背景と、サイドメニューを表示する領域だ。

スクリーンショット 2016-07-03 23.18.35

このサイドメニューの部分のCSSは非表示の時、CSSにpointer-events: none;を指定する。そして、表示されたタイミングでpointer-events: auto;を指定する。

そしてここからが大事な話。左から右、あるいは右から左に移動させる際に、transformを使う。ブラウザがDOMの位置を変更する際に、CPUを使ったレイアウト変更してはいけない。GPUの力を借りて、描画位置を変更することで、最適なパフォーマンスを得ることができる。

例えば、一昔前。サイドメニューが左に消えている時にCSSは

と、left: -102%で隠す。これは一般的な方法だった。しかし、描画を高速に処理できるGPUの恩恵を受けたいなら、transformを使って以下のように記述する。

サイドメニューのDOMのレイアウト位置としては、x位置のleftもy位置のtopも、0のまま。横幅widthも縦幅heightも、100%ということで、全面を覆っているという扱いになる。しかし、transform: translateX(-102%);で描画の位置自体を、左に寄せている。

そして、ここで登場するのがwill-chanage: none;だ。

一昔前にtransform: translateZ(0);をCSSプロパティに指定して、パフォーマンスを改善するというハックが出回ったのをご存知だろうか。このCSSが指定されると、描画には必然的にGPUの力が必要になるため、強制的にGPUに描画を依頼することになる。GPUの恩恵を受けるために活用されたこのバッドノウハウは、will-chanage: transform;という新しいCSSプロパティをWeb標準として追加することによって、同様のことを実現できるようにした。(※注:実態はブラウザ対応の問題もあり、今もtransform: translateZ(0);を使うのが一般的)

ただ、transform: translateZ(0);will-chanage: transform;といったCSS指定は、常時ビデオカード上のRAMメモリーに描画結果をテクスチャーとして保存することになる。モバイル環境では、バッテリー消費などに悪影響を及ぼすことになる。動作するタイミングだけwill-chanage: transform;を指定し、動作しない時は無効化will-chanage: none;するといい。これが、バッテリー消費パフォーマンスと描画速度パフォーマンスのトレードオフ問題に対する、落とし所だ。

スクリーンショット 2016-07-04 0.37.11

黒背景については、will-change: opacity;というプロパティがあり、transformと同様の方法で、高いパフォーマンスで描画させることができる。(※ JSの実装については、「2. Swipeable Cards」にノウハウが似ているので割愛)

2. Swipeable Cards

スクリーンショット 2016-07-03 22.27.37

CSSを使ったパフォーマンス改善のテクニックの他に、注意しなくてはいけないのが、スワイプ操作時のコンポーネントの移動処理。ユーザーからの指の位置状況を入力し、それをスクリーン上に反映しなくてはいけない。この際、有用なのが「ゲームループ」のノウハウだ。

描画のイベントは常に、1/60秒ごとに発生する。対してスワイプのイベントは、常に一定には発生しない。描画のタイミングにはあわせてくれないのだ。

スクリーンショット 2016-07-04 1.05.35

そこで、スワイプにより発生するイベントについては、変数に位置情報だけを記録する。そして、描画時のイベントでは、記録された位置情報を元に、CSSを通じて描画位置変更をおこなう。

スワイプの開始時・移動時・終了時は以下の通り。this.startXthis.currentXthis.targetXといった変数に、現在の位置や、移動すべき位置を記録している。

描画のタイミングにrequestAnimationFrameから呼び出されるコールバックで、先ほどの位置情報を元に反映していく。

(※ この後の処理については、「3. Expand and Collapse」にノウハウが似ているので割愛。)

3. Expand and Collapse

スクリーンショット 2016-07-03 22.27.51

タップすると、領域が広がり全体化されるUIコンポーネント。CSSではどうするのか?もちろん、ここまで説明してきた「transform」を活用する!では、JSについてはどうか?実は、「2. Swipeable Cards」とは異なり、スワイプ操作でなくタップによって、自動的にアニメーションする。この点で、より効率的な実装が求められる。

まず、アニメーションについて、動作中の状態はJS上で持たない。動作前後の状態だけを、CSSプロパティを通じてGPUに指示する。

スクリーンショット 2016-07-04 1.54.47

そのままでは、タップした要素は一瞬にして全体化されてしまう。どのようにして何ミリ秒もかけて徐々に広げていくか?その方法は、CSSで指定する。JSではない。原理的には、従来よく使われているCSSアニメーションだ。

ここまで、Progressive Web Applsのパフォーマンス改善の話をしてきたが、「Google DevelopersのRendering peformance」が役に参考になる。一読するといいだろう。

スクリーンショット 2016-07-04 2.13.00

Progressive Web Appsのパフォーマンス改善。要はこう言いたかった

いかがでしたでしょうか?文字数の制限やコンテキストの高さもあり、多くのエンジニアに伝わるようかなりアレンジしてみましたが、ご理解いただけましたでしょうか?

Paul Lewis氏が言いたかったことは単純な話です。先ほどのGoogle Developersの記事にもありますが、Progressive Web AppsにおけるAnimationやReactionの課題は、いかにしてブラウザのレンダリング処理における「レイアウト」を減らすか、という話です。この講演は、そのTIPS集といえます。

スクリーンショット 2016-07-04 2.19.41

今日のノウハウ、特に新しいというわけでもなく2年前には既に実践されていたことです。実際のところ多くの現場では、OnsenUIやIonicのようなUIライブラリを活用することになり、このあたりの話を意識することはないのでしょう。ただ、Webのサービスを作っているフロントエンドエンジニアにとっては、ライブラリの有無に関係なく知っておくべき知識のように思えます。サイドメニューについては、Webサイトであっても鉄板のUIコンポーネントなので、Progressive Web Appsか否かはもはや関係ないノウハウだったに違いありません。

Webがモバイルに順応していくことは、今後もさらに求められていきます。これは、フレームワークやライブラリに限った話ではなく、トータルにみたWeb、フロントエンドへの要求に変化を与えるに違いません。

今後も、モバイルとWebの関わりに、目が離せませんね。

Powered byNTT Communications

tag list

アクセシビリティ イベント エンタープライズ デザイン ハイブリッド パフォーマンス ブラウザ プログラミング マークアップ モバイル 海外 高速化 Angular2 AngularJS Chrome Cordova CSS de:code ECMAScript Edge Firefox Google Google I/O 2014 HTML5 Conference 2013 html5j IoT JavaScript Microsoft Mozilla Node.js PhoneGap Polymer React Safari SkyWay TypeScript UI UX W3C W3C仕様 Webアプリ Web Components WebGL WebRTC WebSocket