はじめに
読み込み中の更新、ページの切り替えの遅延、タップイベントの定期的な遅延は、今日のモバイルウェブ環境における問題のほんの一部です。デベロッパーは可能な限りネイティブに近づこうとしていますが、ハッキング、リセット、厳格なフレームワークによって、その努力が台無しになることがよくあります。
この記事では、モバイル HTML5 ウェブアプリを作成するために必要な最小限の要素について説明します。主な目的は、今日のモバイル フレームワークが隠そうとしている複雑さを明らかにすることです。このコースでは、最小限のアプローチ(コア HTML5 API の使用)と基本的な基礎を学び、独自のフレームワークを作成したり、現在使用しているフレームワークに貢献したりできるようになります。
ハードウェア アクセラレーション
通常、GPU は詳細な 3D モデリングや CAD 図面を処理しますが、このケースでは、プリミティブな描画(div、背景、ドロップ シャドウ付きのテキスト、画像など)を GPU を介してスムーズに表示し、スムーズにアニメーション化したいと考えています。残念ながら、ほとんどのフロントエンド デベロッパーは、セマンティクスを気にすることなく、このアニメーション プロセスをサードパーティのフレームワークに委ねています。しかし、これらのコア CSS3 機能はマスクされるべきでしょうか?この点に注意することが重要な理由をいくつかご紹介します。
メモリ割り当てと計算負荷 - ハードウェア アクセラレーションのためだけに DOM のすべての要素を合成すると、コードを扱う次の担当者に追いかけられてひどい目に遭う可能性があります。
消費電力 - ハードウェアが起動すると、バッテリーも起動します。モバイル向けに開発する場合、デベロッパーはモバイル ウェブアプリを作成する際に、さまざまなデバイスの制約を考慮する必要があります。ブラウザ メーカーがデバイス ハードウェアへのアクセスをますます有効にするにつれて、この傾向はさらに顕著になるでしょう。
競合 - すでに高速化されているページの一部にハードウェア アクセラレーションを適用すると、動作が不安定になることがありました。そのため、アクセラレーションが重複しているかどうかを把握することは非常に重要です。
ユーザー インタラクションをスムーズにし、ネイティブにできるだけ近づけるには、ブラウザをうまく活用する必要があります。理想的には、モバイル デバイスの CPU が初期アニメーションを設定し、アニメーション プロセス中に GPU がさまざまなレイヤの合成のみを担当するようにします。translate3d、scale3d、translateZ は、アニメーション要素に独自のレイヤを与え、デバイスがすべてをスムーズにレンダリングできるようにします。アクセラレーテッド コンポジティングと WebKit の仕組みについて詳しくは、Ariya Hidayat のブログに多くの有益な情報が掲載されています。
ページの切り替え
モバイル ウェブアプリを開発する際に最も一般的なユーザー インタラクション アプローチである、スライド、フリップ、回転効果の 3 つを見てみましょう。
このコードの動作は、http://slidfast.appspot.com/slide-flip-rotate.html で確認できます(注: このデモはモバイル デバイス向けに作成されているため、エミュレータを起動するか、スマートフォンやタブレットを使用するか、ブラウザ ウィンドウのサイズを 1,024 ピクセル以下に縮小してください)。
まず、スライド、フリップ、回転のトランジションと、それらがどのように高速化されるかを分析します。各アニメーションは CSS と JavaScript の 3 行または 4 行で記述されています。
スライド
3 つのトランジション アプローチの中で最も一般的なスライド ページ トランジションは、モバイル アプリケーションのネイティブな感覚を模倣したものです。スライドの切り替えが呼び出され、新しいコンテンツ領域がビューポートに表示されます。
スライド効果では、まずマークアップを宣言します。
<div id="home-page" class="page">
<h1>Home Page</h1>
</div>
<div id="products-page" class="page stage-right">
<h1>Products Page</h1>
</div>
<div id="about-page" class="page stage-left">
<h1>About Page</h1>
</div>
ステージング ページを左または右に配置するというコンセプトがあることに注目してください。基本的にはどの方向でも構いませんが、この方向が最も一般的です。
これで、わずか数行の CSS でアニメーションとハードウェア アクセラレーションを実現できます。実際のアニメーションは、ページの div 要素のクラスを入れ替えるときに発生します。
.page {
position: absolute;
width: 100%;
height: 100%;
/*activate the GPU for compositing each page */
-webkit-transform: translate3d(0, 0, 0);
}
translate3d(0,0,0) は「銀の弾丸」アプローチと呼ばれます。
ユーザーがナビゲーション要素をクリックすると、次の JavaScript が実行されてクラスが切り替えられます。サードパーティのフレームワークは使用されていません。これは純粋な JavaScript です。;)
function getElement(id) {
return document.getElementById(id);
}
function slideTo(id) {
//1.) the page we are bringing into focus dictates how
// the current page will exit. So let's see what classes
// our incoming page is using. We know it will have stage[right|left|etc...]
var classes = getElement(id).className.split(' ');
//2.) decide if the incoming page is assigned to right or left
// (-1 if no match)
var stageType = classes.indexOf('stage-left');
//3.) on initial page load focusPage is null, so we need
// to set the default page which we're currently seeing.
if (FOCUS_PAGE == null) {
// use home page
FOCUS_PAGE = getElement('home-page');
}
//4.) decide how this focused page should exit.
if (stageType > 0) {
FOCUS_PAGE.className = 'page transition stage-right';
} else {
FOCUS_PAGE.className = 'page transition stage-left';
}
//5. refresh/set the global variable
FOCUS_PAGE = getElement(id);
//6. Bring in the new page.
FOCUS_PAGE.className = 'page transition stage-center';
}
stage-left または stage-right が stage-center になり、ページが中央のビューポートにスライドします。CSS3 に完全に依存して、重い処理を行っています。
.stage-left {
left: -480px;
}
.stage-right {
left: 480px;
}
.stage-center {
top: 0;
left: 0;
}
次に、モバイル デバイスの検出と向きを処理する CSS を見てみましょう。すべてのデバイスとすべての解像度に対応できます(メディアクエリの解像度を参照)。このデモでは、モバイル デバイスの縦向きと横向きのほとんどのビューをカバーするために、いくつかの簡単な例を使用しました。これは、デバイスごとにハードウェア アクセラレーションを適用する場合にも役立ちます。たとえば、WebKit のデスクトップ バージョンは、変換されたすべての要素(2D か 3D かを問わず)を高速化するため、メディア クエリを作成して、そのレベルで高速化を除外するのが妥当です。ハードウェア アクセラレーションのトリックは、Android Froyo 2.2 以降では速度の向上をもたらしません。すべてのコンポジションはソフトウェア内で行われます。
/* iOS/android phone landscape screen width*/
@media screen and (max-device-width: 480px) and (orientation:landscape) {
.stage-left {
left: -480px;
}
.stage-right {
left: 480px;
}
.page {
width: 480px;
}
}
フリッピング
モバイル デバイスでは、フリップはページを実際にスワイプして閉じる操作を指します。ここでは、簡単な JavaScript を使用して、iOS デバイスと Android デバイス(WebKit ベース)でこのイベントを処理します。
実際の動作については、http://slidfast.appspot.com/slide-flip-rotate.html をご覧ください。
タッチイベントとトランジションを扱う場合、まず要素の現在位置を把握する必要があります。WebKitCSSMatrix について詳しくは、こちらのドキュメントをご覧ください。
function pageMove(event) {
// get position after transform
var curTransform = new WebKitCSSMatrix(window.getComputedStyle(page).webkitTransform);
var pagePosition = curTransform.m41;
}
ページめくりに CSS3 のイーズアウト トランジションを使用しているため、通常の element.offsetLeft は機能しません。
次に、ユーザーがフリップしている方向を特定し、イベント(ページ ナビゲーション)が発生するしきい値を設定します。
if (pagePosition >= 0) {
//moving current page to the right
//so means we're flipping backwards
if ((pagePosition > pageFlipThreshold) || (swipeTime < swipeThreshold)) {
//user wants to go backward
slideDirection = 'right';
} else {
slideDirection = null;
}
} else {
//current page is sliding to the left
if ((swipeTime < swipeThreshold) || (pagePosition < pageFlipThreshold)) {
//user wants to go forward
slideDirection = 'left';
} else {
slideDirection = null;
}
}
また、swipeTime もミリ秒単位で測定していることがわかります。これにより、ユーザーが画面をすばやくスワイプしてページをめくった場合に、ナビゲーション イベントが発火します。
ページを配置し、指が画面に触れている間、アニメーションがネイティブに見えるように、各イベントの発生後に CSS3 トランジションを使用します。
function positionPage(end) {
page.style.webkitTransform = 'translate3d('+ currentPos + 'px, 0, 0)';
if (end) {
page.style.WebkitTransition = 'all .4s ease-out';
//page.style.WebkitTransition = 'all .4s cubic-bezier(0,.58,.58,1)'
} else {
page.style.WebkitTransition = 'all .2s ease-out';
}
page.style.WebkitUserSelect = 'none';
}
キュービック ベジエを試して、トランジションにネイティブな感覚を出すようにしましたが、イーズアウトでうまくいきました。
最後に、ナビゲーションを実行するには、前回のデモで使用した、以前に定義した slideTo() メソッドを呼び出す必要があります。
track.ontouchend = function(event) {
pageMove(event);
if (slideDirection == 'left') {
slideTo('products-page');
} else if (slideDirection == 'right') {
slideTo('home-page');
}
}
回転
次に、このデモで使用されている回転アニメーションを見てみましょう。[連絡先] メニュー オプションをタップすると、現在表示しているページを 180 度回転させて裏面を表示できます。ここでも、CSS と JavaScript を数行記述するだけで、遷移クラス onclick を割り当てることができます。注: ほとんどのバージョンの Android では、3D CSS 変換機能がないため、回転トランジションが正しくレンダリングされません。残念ながら、Android は反転を無視するのではなく、反転ではなく回転によってページを「カートホイール」のように移動させます。サポートが改善されるまでは、このトランジションの使用は控えめにするようおすすめします。
マークアップ(フロントとバックの基本的なコンセプト):
<div id="front" class="normal">
...
</div>
<div id="back" class="flipped">
<div id="contact-page" class="page">
<h1>Contact Page</h1>
</div>
</div>
JavaScript:
function flip(id) {
// get a handle on the flippable region
var front = getElement('front');
var back = getElement('back');
// again, just a simple way to see what the state is
var classes = front.className.split(' ');
var flipped = classes.indexOf('flipped');
if (flipped >= 0) {
// already flipped, so return to original
front.className = 'normal';
back.className = 'flipped';
FLIPPED = false;
} else {
// do the flip
front.className = 'flipped';
back.className = 'normal';
FLIPPED = true;
}
}
CSS:
/*----------------------------flip transition */
#back,
#front {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
-webkit-transition-duration: .5s;
-webkit-transform-style: preserve-3d;
}
.normal {
-webkit-transform: rotateY(0deg);
}
.flipped {
-webkit-user-select: element;
-webkit-transform: rotateY(180deg);
}
ハードウェア アクセラレーションのデバッグ
基本的なトランジションについて説明したので、次はトランジションの仕組みと合成について見ていきましょう。
この魔法のようなデバッグ セッションを実現するには、ブラウザを 2 つと、任意の IDE を起動します。まず、コマンドラインから Safari を起動して、いくつかのデバッグ環境変数を使用します。私は Mac を使用しているため、コマンドは OS によって異なる場合があります。ターミナルを開いて、次のように入力します。
- $> export CA_COLOR_OPAQUE=1
- $> export CA_LOG_MEMORY_USAGE=1
- $> /Applications/Safari.app/Contents/MacOS/Safari
これにより、いくつかのデバッグ ヘルパーを使用して Safari が起動します。CA_COLOR_OPAQUE は、実際に合成または高速化された要素を示します。CA_LOG_MEMORY_USAGE は、描画オペレーションをバッキング ストアに送信する際に使用しているメモリの量を示します。これにより、モバイル デバイスにどの程度の負荷がかかっているかを正確に把握できます。また、GPU の使用状況がターゲット デバイスのバッテリーを消耗させている可能性についても把握できます。
Chrome を起動して、良好なフレーム/秒(FPS)情報を確認してみましょう。
- Google Chrome ウェブブラウザを開きます。
- URL バーに「about:flags」と入力します。
- 数項目下にスクロールし、[FPS カウンタ] の [有効にする] をクリックします。
強化版の Chrome でこのページを表示すると、左上に赤い FPS カウンタが表示されます。
これで、ハードウェア アクセラレーションがオンになっていることがわかります。また、アニメーションの実行方法や、リーク(停止すべきアニメーションが継続的に実行されている状態)があるかどうかを把握することもできます。
ハードウェア アクセラレーションを実際に視覚化するもう 1 つの方法は、Safari で同じページを開くことです(上記の環境変数を使用)。アクセラレートされた DOM 要素はすべて赤みがかった色になります。これにより、レイヤごとに何が合成されているかを正確に把握できます。白いナビゲーションは高速化されていないため、赤色になっていないことに注意してください。
Chrome の同様の設定は、about:flags の「合成されたレンダリング レイヤの境界線」でも利用できます。
合成されたレイヤを確認するもう 1 つの方法は、この mod を適用した状態で WebKit の落ち葉のデモを表示することです。
最後に、アプリケーションのグラフィック ハードウェアのパフォーマンスを正しく把握するために、メモリの使用状況を見てみましょう。ここでは、Mac OS の CoreAnimation バッファに 1.38 MB の描画命令がプッシュされていることがわかります。Core Animation のメモリバッファは、OpenGL ES と GPU の間で共有され、画面に表示される最終的なピクセルが作成されます。
ブラウザ ウィンドウのサイズを変更したり、最大化したりすると、メモリも拡張されます。
ブラウザのサイズを正しい寸法に変更した場合にのみ、モバイル デバイスでのメモリ使用状況を把握できます。iPhone 環境のデバッグやテストを行っていた場合は、480 ピクセル × 320 ピクセルにサイズ変更します。ハードウェア アクセラレーションの仕組みとデバッグに必要な手順を正確に把握できました。GPU メモリ バッファの動作を視覚的に確認することで、実際に動作している様子を把握できます。
舞台裏: フェッチとキャッシュ保存
では、ページとリソースのキャッシュ保存を次のレベルに進めましょう。JQuery Mobile や同様のフレームワークで使用されているアプローチと同様に、同時 AJAX 呼び出しを使用してページをプリフェッチしてキャッシュに保存します。
モバイルウェブの主な問題と、それに対処する必要がある理由をいくつかご紹介します。
- フェッチ: ページをプリフェッチすることで、ユーザーはアプリをオフラインで使用でき、ナビゲーション アクション間の待ち時間もなくなります。もちろん、デバイスがオンラインになったときに帯域幅を制限したくはないので、この機能は控えめに使用する必要があります。
- キャッシュ保存: 次に、これらのページを取得してキャッシュに保存する際に、同時実行または非同期のアプローチが必要になります。また、localStorage(デバイス間で十分にサポートされているため)を使用する必要がありますが、残念ながら非同期ではありません。
- AJAX とレスポンスの解析: innerHTML() を使用して AJAX レスポンスを DOM に挿入するのは危険です(また、信頼性も低い可能性があります)。代わりに、AJAX レスポンスの挿入と同時呼び出しの処理に信頼性の高いメカニズムを使用します。また、
xhr.responseTextの解析には HTML5 の新機能も活用しています。
スライド、フリップ、回転のデモのコードを基に、まずいくつかのセカンダリ ページを追加して、それらにリンクします。リンクを解析し、その場でトランジションを作成します。
ご覧のとおり、ここではセマンティック マークアップを活用しています。別のページへのリンクです。子ページは、親ページと同じノード/クラス構造に従います。さらに、data-* 属性を「ページ」ノードなどに使用することもできます。以下は、別の HTML ファイル(/demo2/home-detail.html)にある詳細ページ(子)です。このページは、アプリの読み込み時に読み込まれ、キャッシュに保存され、トランジション用に設定されます。
<div id="home-page" class="page">
<h1>Home Page</h1>
<a href="demo2/home-detail.html" class="fetch">Find out more about the home page!</a>
</div>
では、JavaScript を見てみましょう。わかりやすくするため、ヘルパーや最適化はコードから除外しています。ここでは、指定された DOM ノードの配列をループ処理して、フェッチしてキャッシュに保存するリンクを抽出しています。注 - このデモでは、このメソッド fetchAndCache() はページの読み込み時に呼び出されます。次のセクションでは、ネットワーク接続を検出し、呼び出すタイミングを判断する際に、この処理をやり直します。
var fetchAndCache = function() {
// iterate through all nodes in this DOM to find all mobile pages we care about
var pages = document.getElementsByClassName('page');
for (var i = 0; i < pages.length; i++) {
// find all links
var pageLinks = pages[i].getElementsByTagName('a');
for (var j = 0; j < pageLinks.length; j++) {
var link = pageLinks[j];
if (link.hasAttribute('href') &&
//'#' in the href tells us that this page is already loaded in the DOM - and
// that it links to a mobile transition/page
!(/[\#]/g).test(link.href) &&
//check for an explicit class name setting to fetch this link
(link.className.indexOf('fetch') >= 0)) {
//fetch each url concurrently
var ai = new ajax(link,function(text,url){
//insert the new mobile page into the DOM
insertPages(text,url);
});
ai.doGet();
}
}
}
};
「AJAX」オブジェクトを使用して、非同期の事後処理が適切に行われるようにします。AJAX 呼び出し内で localStorage を使用するより高度な説明については、HTML5 オフラインでオフグリッドで作業するをご覧ください。この例では、各リクエストでキャッシュを使用し、サーバーが成功(200)以外のレスポンスを返したときにキャッシュに保存されたオブジェクトを提供する基本的な使用方法を示します。
function processRequest () {
if (req.readyState == 4) {
if (req.status == 200) {
if (supports_local_storage()) {
localStorage[url] = req.responseText;
}
if (callback) callback(req.responseText,url);
} else {
// There is an error of some kind, use our cached copy (if available).
if (!!localStorage[url]) {
// We have some data cached, return that to the callback.
callback(localStorage[url],url);
return;
}
}
}
}
残念ながら、localStorage は文字エンコードに UTF-16 を使用するため、各バイトは 2 バイトとして保存され、ストレージの上限は 5 MB から 2.6 MB になります。これらのページ/マークアップをアプリケーション キャッシュのスコープ外で取得してキャッシュに保存する理由については、次のセクションで説明します。
HTML5 の iframe 要素の最近の進歩により、AJAX 呼び出しから返される responseText を解析する簡単で効果的な方法ができました。3,000 行の JavaScript パーサーや、スクリプトタグなどを削除する正規表現はたくさんあります。ブラウザの得意な処理はブラウザに任せるべきです。この例では、responseText を一時的な非表示の iframe に書き込みます。スクリプトを無効にして多くのセキュリティ機能を提供する HTML5 の「sandbox」属性を使用しています。
仕様より: sandbox 属性が指定されている場合、iframe でホストされているコンテンツに一連の追加の制限が適用されます。その値は、ASCII 大文字と小文字を区別しない、固有のスペース区切りトークンの順序なしセットでなければなりません。指定できる値は、allow-forms、allow-same-origin、allow-scripts、allow-top-navigation です。この属性が設定されると、コンテンツは一意のオリジンからのものとして扱われ、フォームとスクリプトが無効になり、リンクが他のブラウジング コンテキストをターゲットに設定できなくなり、プラグインが無効になります。
var insertPages = function(text, originalLink) {
var frame = getFrame();
//write the ajax response text to the frame and let
//the browser do the work
frame.write(text);
//now we have a DOM to work with
var incomingPages = frame.getElementsByClassName('page');
var pageCount = incomingPages.length;
for (var i = 0; i < pageCount; i++) {
//the new page will always be at index 0 because
//the last one just got popped off the stack with appendChild (below)
var newPage = incomingPages[0];
//stage the new pages to the left by default
newPage.className = 'page stage-left';
//find out where to insert
var location = newPage.parentNode.id == 'back' ? 'back' : 'front';
try {
// mobile safari will not allow nodes to be transferred from one DOM to another so
// we must use adoptNode()
document.getElementById(location).appendChild(document.adoptNode(newPage));
} catch(e) {
// todo graceful degradation?
}
}
};
Safari は、ノードをあるドキュメントから別のドキュメントに暗黙的に移動することを正しく拒否します。新しい子ノードが別のドキュメントで作成された場合、エラーが発生します。ここでは adoptNode を使用します。
では、なぜ iframe を使用するのでしょうか?innerHTML を使用しないのはなぜですか?innerHTML は HTML5 仕様の一部になりましたが、サーバー(悪意のあるものも含む)からのレスポンスをチェックされていない領域に挿入するのは危険な行為です。この記事の執筆中、innerHTML 以外のものを使用している人は見つかりませんでした。JQuery はコアでこれを使用しており、例外が発生した場合にのみ append フォールバックを使用していることは知っています。また、JQuery Mobile もこれを使用します。ただし、innerHTML の 「ランダムに動作しなくなる」という点については、徹底的なテストを行っていません。この問題が影響するすべてのプラットフォームを確認できれば、非常に興味深いでしょう。どちらのアプローチがよりパフォーマンスが高いかを確認することも興味深いでしょう。この点については、両方の主張を聞いたことがあります。
ネットワークの種類の検出、処理、プロファイリング
ウェブアプリをバッファリング(または予測キャッシュ)できるようになったので、アプリをよりスマートにする適切な接続検出機能を提供する必要があります。モバイルアプリ開発では、オンライン/オフライン モードと接続速度が非常に重要になります。The Network Information API と入力します。この機能をプレゼンテーションで紹介するたびに、聴衆の誰かが手を挙げて「これは何に使うのですか?」と質問します。そこで、非常にスマートなモバイル ウェブアプリを設定する方法をご紹介します。
まず、当たり前のシナリオから説明します。高速鉄道でモバイル デバイスからウェブにアクセスしている場合、ネットワークがさまざまなタイミングで切断される可能性があり、地域によってサポートされる伝送速度が異なる可能性があります(例: HSPA または 3G は一部の都市部で利用できる可能性がありますが、遠隔地では 2G テクノロジーのほうが一般的です。次のコードは、ほとんどの接続シナリオに対応しています。
次のコードは、
applicationCacheを介したオフライン アクセス。- ブックマークされているかどうか、オフラインかどうかを検出します。
- オフラインからオンラインへの切り替え、またはその逆を検出します。
- 接続が遅いことを検出し、ネットワーク タイプに基づいてコンテンツを取得します。
これらの機能は、いずれもコードをほとんど必要としません。まず、イベントと読み込みシナリオを検出します。
window.addEventListener('load', function(e) {
if (navigator.onLine) {
// new page load
processOnline();
} else {
// the app is probably already cached and (maybe) bookmarked...
processOffline();
}
}, false);
window.addEventListener("offline", function(e) {
// we just lost our connection and entered offline mode, disable eternal link
processOffline(e.type);
}, false);
window.addEventListener("online", function(e) {
// just came back online, enable links
processOnline(e.type);
}, false);
上記の EventListeners では、イベントから呼び出されているのか、実際のページ リクエストまたは更新から呼び出されているのかをコードに伝える必要があります。主な理由は、オンライン モードとオフライン モードを切り替えるときに、body onload イベントが発火しないためです。
次に、ononline イベントまたは onload イベントの簡単なチェックを行います。このコードは、オフラインからオンラインに切り替えるときに無効なリンクをリセットしますが、このアプリがより高度なものであれば、コンテンツの取得を再開したり、断続的な接続の UX を処理したりするロジックを挿入する可能性があります。
function processOnline(eventType) {
setupApp();
checkAppCache();
// reset our once disabled offline links
if (eventType) {
for (var i = 0; i < disabledLinks.length; i++) {
disabledLinks[i].onclick = null;
}
}
}
processOffline() も同様です。ここでは、オフライン モード用にアプリを操作し、バックグラウンドで進行していたトランザクションの復元を試みます。以下のコードは、すべての外部リンクを掘り出して無効にし、ユーザーをオフライン アプリに永遠に閉じ込めます。
function processOffline() {
setupApp();
// disable external links until we come back - setting the bounds of app
disabledLinks = getUnconvertedLinks(document);
// helper for onlcick below
var onclickHelper = function(e) {
return function(f) {
alert('This app is currently offline and cannot access the hotness');return false;
}
};
for (var i = 0; i < disabledLinks.length; i++) {
if (disabledLinks[i].onclick == null) {
//alert user we're not online
disabledLinks[i].onclick = onclickHelper(disabledLinks[i].href);
}
}
}
では、本題に入りましょう。アプリが接続状態を認識できるようになったので、オンラインのときに接続の種類を確認し、それに応じて調整することもできます。各接続のコメントに、北米の一般的なプロバイダのダウンロード速度とレイテンシを記載しました。
function setupApp(){
// create a custom object if navigator.connection isn't available
var connection = navigator.connection || {'type':'0'};
if (connection.type == 2 || connection.type == 1) {
//wifi/ethernet
//Coffee Wifi latency: ~75ms-200ms
//Home Wifi latency: ~25-35ms
//Coffee Wifi DL speed: ~550kbps-650kbps
//Home Wifi DL speed: ~1000kbps-2000kbps
fetchAndCache(true);
} else if (connection.type == 3) {
//edge
//ATT Edge latency: ~400-600ms
//ATT Edge DL speed: ~2-10kbps
fetchAndCache(false);
} else if (connection.type == 2) {
//3g
//ATT 3G latency: ~400ms
//Verizon 3G latency: ~150-250ms
//ATT 3G DL speed: ~60-100kbps
//Verizon 3G DL speed: ~20-70kbps
fetchAndCache(false);
} else {
//unknown
fetchAndCache(true);
}
}
fetchAndCache プロセスにはさまざまな調整を加えることができますが、ここでは、特定の接続に対してリソースを非同期(true)または同期(false)で取得するように指示しただけです。
Edge(同期)リクエスト タイムライン
WIFI(非同期)リクエストのタイムライン
これにより、接続速度に応じてユーザー エクスペリエンスを調整する少なくとも何らかの方法が可能になります。これは万能のソリューションではありません。もう 1 つの課題は、アプリがバックグラウンドでリンク先のページを取得している間、リンクがクリックされたときに(接続が遅い場合)読み込みモーダルを表示することです。ここで重要なのは、最新の HTML5 の機能を最大限に活用しながら、レイテンシを削減することです。ネットワーク検出のデモはこちらでご覧ください。
まとめ
モバイル HTML5 アプリの道のりは始まったばかりです。これで、HTML5 とそのサポート技術のみで構築されたモバイル「フレームワーク」の非常にシンプルで基本的な基盤がわかりました。デベロッパーは、これらの機能をラッパーで隠すのではなく、そのコアで扱い、対処することが重要だと思います。