Введение
Вращающиеся обновления, прерывистые переходы между страницами и периодические задержки при нажатии — лишь некоторые из проблем современных мобильных веб-сред. Разработчики стараются максимально приблизиться к нативному интерфейсу, но часто сталкиваются с хаками, сбросами настроек и жёсткими фреймворками.
В этой статье мы обсудим самый минимум того, что необходимо для создания мобильного веб-приложения на HTML5. Главная цель — выявить скрытые сложности, которые пытаются скрыть современные мобильные фреймворки. Вы увидите минималистичный подход (с использованием основных API HTML5) и базовые принципы, которые позволят вам написать собственный фреймворк или дополнить тот, который вы используете.
Аппаратное ускорение
Обычно графические процессоры обрабатывают детальное 3D-моделирование или CAD-схемы, но в данном случае мы хотим, чтобы наши примитивные рисунки (div-элементы, фоны, текст с тенями, изображения и т. д.) отображались плавно и анимировались с помощью графического процессора. К сожалению, большинство разработчиков интерфейсов перекладывают этот процесс анимации на сторонние фреймворки, не заботясь о семантике. Но стоит ли маскировать эти основные функции CSS3? Позвольте мне привести несколько причин, почему важно уделять этому внимание:
Выделение памяти и вычислительная нагрузка — если вы занимаетесь компоновкой каждого элемента в DOM только ради аппаратного ускорения, следующий человек, который будет работать над вашим кодом, может догнать вас и жестоко избить.
Энергопотребление — Очевидно, что когда аппаратное обеспечение начинает работать, разряжается и аккумулятор. При разработке мобильных приложений разработчикам приходится учитывать широкий спектр ограничений устройств. Это будет становиться всё более актуальным, поскольку производители браузеров начинают предоставлять доступ ко всё большему количеству устройств.
Конфликты — я столкнулся с проблемами при применении аппаратного ускорения к тем частям страницы, которые уже были ускорены. Поэтому очень важно знать, есть ли у вас перекрывающееся ускорение.
Чтобы взаимодействие с пользователем было плавным и максимально приближенным к нативному, мы должны заставить браузер работать за нас. В идеале мы хотим, чтобы центральный процессор мобильного устройства настраивал начальную анимацию, а графический процессор отвечал только за композицию различных слоёв в процессе анимации. Именно это делают translate3d, scale3d и translateZ — они выделяют анимированным элементам отдельный слой, позволяя устройству плавно визуализировать всё вместе. Чтобы узнать больше об ускоренной композиции и о работе WebKit, Ария Хидаят опубликовал много полезной информации в своём блоге .
Переходы страниц
Давайте рассмотрим три наиболее распространенных подхода к взаимодействию с пользователем при разработке мобильного веб-приложения: эффекты скольжения, переворачивания и вращения.
Вы можете увидеть этот код в действии здесь http://slidfast.appspot.com/slide-flip-rotate.html (Примечание: эта демонстрация создана для мобильного устройства, поэтому запустите эмулятор, используйте свой телефон или планшет либо уменьшите размер окна браузера до ~1024 пикселей или меньше).
Сначала мы разберём переходы скольжения, переворачивания и поворота и то, как они ускоряются. Обратите внимание, что каждая анимация занимает всего три-четыре строки CSS и JavaScript.
Раздвижные
Самый распространённый из трёх подходов к переходу — скользящие переходы страниц, имитирующие нативный стиль мобильных приложений. Скользящий переход используется для добавления новой области контента в область просмотра.
Для эффекта скольжения сначала мы объявляем нашу разметку:
<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 ускоряет все преобразованные элементы (независимо от того, являются ли они двумерными или трёхмерными), имеет смысл создать медиа-запрос и исключить ускорение на этом уровне. Обратите внимание, что приёмы аппаратного ускорения не обеспечивают никакого улучшения скорости в 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-переход с функцией ease-out, обычный 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';
}
Я попытался поиграться с cube-bezier, чтобы придать переходам максимально естественный вид, но с помощью ease-out все получилось.
Наконец, чтобы реализовать навигацию, мы должны вызвать ранее определенные нами методы slideTo()
, которые мы использовали в последней демонстрации.
track.ontouchend = function(event) {
pageMove(event);
if (slideDirection == 'left') {
slideTo('products-page');
} else if (slideDirection == 'right') {
slideTo('home-page');
}
}
Вращающийся
Далее давайте рассмотрим анимацию поворота, используемую в этой демонстрации. Вы можете в любой момент повернуть просматриваемую страницу на 180 градусов, чтобы увидеть её обратную сторону, нажав на пункт меню «Контакты». Опять же, для назначения класса перехода onclick
потребуется всего несколько строк CSS и немного JavaScript. ПРИМЕЧАНИЕ: Переход с поворотом отображается некорректно в большинстве версий 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);
}
Отладка аппаратного ускорения
Теперь, когда мы рассмотрели основные переходы, давайте рассмотрим механизмы их работы и компоновки.
Чтобы запустить этот волшебный сеанс отладки, давайте запустим пару браузеров и предпочитаемую вами IDE. Сначала запустите Safari из командной строки, чтобы использовать некоторые переменные среды отладки. Я работаю на Mac, поэтому команды могут отличаться в зависимости от вашей ОС. Откройте Терминал и введите следующее:
- $> экспорт CA_COLOR_OPAQUE=1
- $> экспорт CA_LOG_MEMORY_USAGE=1
- $> /Applications/Safari.app/Contents/MacOS/Safari
Это запускает Safari с несколькими вспомогательными функциями отладки. CA_COLOR_OPAQUE показывает, какие элементы фактически компонуются или ускоряются. CA_LOG_MEMORY_USAGE показывает, сколько памяти используется при отправке операций рисования в резервное хранилище . Это позволяет точно определить нагрузку на мобильное устройство и, возможно, подсказать, как использование графического процессора может разряжать аккумулятор целевого устройства.
Теперь давайте запустим Chrome, чтобы увидеть некоторую информацию о количестве кадров в секунду (FPS):
- Откройте веб-браузер Google Chrome.
- В адресной строке введите about:flags .
- Прокрутите вниз несколько пунктов и нажмите «Включить» для счетчика FPS.
Если вы просматриваете эту страницу в улучшенной версии Chrome, в левом верхнем углу вы увидите красный счетчик FPS.

Это позволяет нам определить, включено ли аппаратное ускорение. Это также даёт нам представление о том, как работает анимация и есть ли утечки (непрерывно работающие анимации, которые следует остановить).
Другой способ наглядно представить аппаратное ускорение — открыть ту же страницу в Safari (с упомянутыми выше переменными окружения). Каждый ускоренный элемент DOM имеет красный оттенок. Это показывает, что именно компонуется по слоям. Обратите внимание, что белая панель навигации не красная, потому что она не ускорена.

Аналогичная настройка для Chrome также доступна в about:flags «Границы составного слоя рендеринга».
Еще один отличный способ увидеть скомпонованные слои — просмотреть демоверсию падающих листьев WebKit при применении этого мода.

И наконец, чтобы по-настоящему оценить производительность графического оборудования нашего приложения, давайте посмотрим, как используется память. Здесь мы видим, что мы передаем 1,38 МБ инструкций отрисовки в буферы CoreAnimation в Mac OS. Буферы памяти Core Animation совместно используются OpenGL ES и графическим процессором для создания финальных пикселей, которые вы видите на экране.

Когда мы просто изменяем размер или разворачиваем окно браузера, мы видим, что память также расширяется.

Это даёт представление о потреблении памяти на мобильном устройстве, только если вы измените размер окна браузера до корректных. Если вы отлаживали или тестировали среду iPhone, измените размер до 480x320 пикселей. Теперь мы точно понимаем, как работает аппаратное ускорение и что требуется для отладки. Одно дело читать об этом, но увидеть, как работают буферы памяти графического процессора, действительно проливает свет на ситуацию.
За кулисами: извлечение и кэширование
Теперь пришло время вывести кэширование страниц и ресурсов на новый уровень. Подобно подходу, используемому JQuery Mobile и аналогичными фреймворками, мы будем предварительно загружать и кэшировать страницы с помощью одновременных AJAX-вызовов.
Давайте рассмотрим несколько основных проблем мобильного Интернета и причины, по которым нам нужно это сделать:
- Загрузка: Предварительная загрузка страниц позволяет пользователям отключать приложение от сети и не ждать между действиями навигации. Конечно, мы не хотим перегружать пропускную способность устройства, когда оно подключается к сети, поэтому эту функцию следует использовать экономно.
- Кэширование: Далее нам нужен параллельный или асинхронный подход к выборке и кэшированию этих страниц. Также необходимо использовать localStorage (поскольку он хорошо поддерживается устройствами), который, к сожалению, не поддерживает асинхронный режим.
- AJAX и парсинг ответа: Использование innerHTML() для вставки AJAX-ответа в DOM опасно (и ненадёжно ?). Вместо этого мы используем надёжный механизм вставки AJAX-ответа и обработки параллельных вызовов. Мы также используем некоторые новые функции HTML5 для парсинга
xhr.responseText
.
Основываясь на коде из демо-ролика «Скольжение, переворачивание и поворот» , мы начнём с добавления нескольких дополнительных страниц и ссылок на них. Затем мы будем анализировать ссылки и создавать переходы «на лету».

Демонстрацию Fetch and Cache можно посмотреть здесь.
Как видите, здесь мы используем семантическую разметку. Это просто ссылка на другую страницу. Дочерняя страница следует той же структуре узлов/классов, что и родительская. Мы могли бы пойти ещё дальше и использовать атрибут 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». Более подробное объяснение использования localStorage в вызове AJAX приведено в статье «Работа вне сетки с 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 МБ до 2,6 МБ . Полная причина извлечения и кэширования этих страниц/разметки вне области действия кэша приложения раскрывается в следующем разделе.
Благодаря недавним достижениям в области элемента iframe в HTML5 у нас появился простой и эффективный способ парсить responseText
, возвращаемый нашим AJAX-вызовом. Существует множество парсеров JavaScript длиной в 3000 строк и регулярных выражений, которые удаляют теги скриптов и т.д. Но почему бы не позволить браузеру делать то, что он умеет лучше всего? В этом примере мы запишем 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 использует его в своей основе с резервным добавлением только при исключении. И JQuery Mobile тоже использует его. Однако я не проводил никаких серьезных тестов на предмет того, что innerHTML «перестает работать случайным образом» , но было бы очень интересно увидеть все платформы, на которые это влияет. Было бы также интересно узнать, какой подход более производительный... Я слышал заявления с обеих сторон по этому поводу.
Обнаружение, обработка и профилирование типа сети
Теперь, когда у нас есть возможность буферизации (или предиктивного кэширования) нашего веб-приложения, мы должны обеспечить надлежащие функции определения соединения, которые сделают его умнее. Именно здесь разработка мобильных приложений становится чрезвычайно чувствительной к онлайн/офлайн режимам и скорости соединения. Встречайте 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
. Этот код сбрасывает отключенные ссылки при переключении из офлайн-режима в онлайн-режим, но если бы приложение было более сложным, вы могли бы добавить логику, которая возобновляла бы загрузку контента или управляла бы пользовательским интерфейсом при прерываниях подключения.
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 (Synchronous)

Временная шкала запросов WIFI (асинхронных)

Это позволяет как минимум в какой-то степени адаптировать пользовательский интерфейс к скорости или скорости соединения. Это ни в коем случае не является универсальным решением. Другой вариант — отображать модальное окно загрузки при нажатии на ссылку (при медленном соединении), пока приложение всё ещё может загружать страницу по этой ссылке в фоновом режиме. Главная задача — сократить задержки, используя все возможности пользовательского соединения с помощью новейшей и лучшей версии HTML5. Посмотреть демонстрацию обнаружения сети можно здесь .
Заключение
Путь мобильных HTML5-приложений только начинается. Теперь вы видите очень простую и базовую основу мобильного «фреймворка», построенного исключительно на HTML5 и поддерживающих его технологиях. Я считаю, что разработчикам важно работать с этими функциями и реализовывать их в их основе, а не скрывать их за внешними оболочками.