Давайте примем коллективное решение здесь. Мы перестанем относиться к мобильным телефонам класса A как к пришельцам и увидим их такими, какие они есть: мощные, способные устройства, работающие с мощными механизмами рендеринга.
Создание в вашем приложении фреймворка, такого как jQuery Mobile, Sencha или чего-либо, что дает вам популярный внешний вид, похожий на iOS, мгновенно предлагает вам выбор, дизайн и функциональность. Ваше приложение превращается в гамбургер McDonalds: то же самое, что и куча других приложений B-side, которые выглядят и чувствуют себя немного хуже, чем нативное приложение. Вы можете сделать это намного лучше, используя то, что обеспечивает браузер на каждом из этих устройств.
Мы рассмотрим, как это сделать, используя минималистский подход с использованием HTML5, CSS3 и Backbone.js . Минимализм сейчас моден (ваш пробег может отличаться) для настольных веб-приложений, но для мобильных устройств преимущества огромны, а главное, заметны.
Укажите свой iPhone / Android на http://bm-example.heroku.com и поиграйте. Этот пример взят из приложения, над которым я работал очень медленно, уже некоторое время. Реализация, в свою очередь, основана на примере, который поставляется с Zepto.js .
Перво наперво
Давайте сначала разберемся с разметкой. Каждая «страница» в нашем приложении определяется тегом <section>
. Навигация будет заключаться в том, что мы будем переходить из одного раздела в другой, поэтому в любой момент времени виден только один раздел, то есть то, как практически каждый мобильный веб-фреймворк работает в реальности, хотя способ, которым мы это делаем, довольно минимален и семантичен.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> </head> <body> <section id="home"></section> <section id="settings"></section> <section id="shop-1"></section> </body> </html>
Вот часть CSS, которая обрабатывает навигацию. Я пропустил эстетические части, чтобы сфокусировать обсуждение, но вы можете увидеть весь код, а не только CSS, в репозитории BuildMobile GitHub Backbone.js .
* { margin: 0; padding: 0; outline: 0; -webkit-box-sizing: border-box } body { position: absolute; height: 100%; width: 100%; overflow-x: hidden; -webkit-perspective: 800; -webkit-text-size-adjust: none; -webkit-transform-style: preserve-3d; -webkit-user-select: none; } body > section { position: absolute; left: 0; width: 100%; opacity: 0; -webkit-transition: all 0.25s ease-in-out; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(100%,0%,0%); } body > section.current { -webkit-transform: translate3d(0%,0%, 0%); opacity: 1 }
Выше сказано примерно:
- Для каждого элемента сбросьте поля, отступы и контур на ноль. Кроме того, пусть ширина элемента определяется тем, где находится граница, а не его содержимым. Этот последний параметр особенно удобен в контексте мобильных устройств, поскольку будет много элементов, позволяющих свободно перемещаться по максимальной ширине экрана. Если бы мы добавили отступы к любому из них и исключили это, произойдет переполнение.
- Растяните тело до максимальной ширины, и каждый ближайший дочерний раздел вместе с ним.
- По умолчанию каждый раздел скрыт. Будет виден только раздел, содержащий
current
класс. - Мы хотим разрешить вертикальную прокрутку, сохраняя горизонтальную прокрутку.
- Переход должен происходить для каждого переходного атрибута. Это поможет нам создать крутой эффект перехода между страницами.
Звучит как-то безумно, но это действительно все, что нужно нашему приложению в части структурирования его разделов. Но пока мы не можем нажимать кнопки и переходить из одного места в другое, у нас не так много. Итак, давайте продолжим с Backbone Router и некоторым действием pushState
.
var Router = Backbone.Router.extend({ routes : { '' : 'home', '/' : 'home', '/shops/:id' : 'shop', '/:id' : 'page' }, hideAll : function() { $('body > section').removeClass('current'); }, home : function() { this.hideAll(); $('#home').addClass('current'); }, page : function(id) { this.hideAll(); $('#' + id).addClass('current'); } }); App.placeAnchorHooks(); Backbone.history.start({ pushState: true });
Ранее я говорил, что только один раздел будет виден одновременно. Это достигается путем присвоения current
класса CSS видимому разделу. Поскольку по умолчанию каждый раздел скрыт, согласно CSS, который мы написали ранее, все остальные разделы остаются скрытыми. Метод hideAll
удаляет current
класс из любого раздела, в котором он есть, поэтому мы вызываем его сначала, прежде чем назначить его любому другому разделу.
Мы определили три маршрута. Маршрут для домашней страницы, который является первым, что вы увидите при входе в приложение, маршрут для сопоставления магазинов, которые мы просматриваем, и универсальный маршрут, который активируется, когда мы указываем что-то еще.
Обратите внимание, что если это приложение типа брошюры (только отдельные разделы и навигация), нам не понадобится ничего от Backbone, кроме маршрутизатора. Это одна большая поддержка для духа минимализма за этой структурой. Вы можете использовать части, которые вы хотите, оставляя при этом другие.
соединение
Поскольку мы используем pushState , мы не говорим о ссылках в техническом смысле. Они не являются якорями, которые используют атрибут href
чтобы заставить браузер сделать новый запрос, pushState
— это нечто иное: вы полагаетесь на JavaScript, чтобы манипулировать историей браузера, pushState
в него URL-адреса. Backbone, в свою очередь, обнаруживает эти изменения и отправляет вас на «маршрут», который соответствует отправляемому URL.
Чтобы было понятно: забудьте про настоящие якоря. Если вы не заинтересованы в том, чтобы приложение ухудшилось до браузеров, которые не поддерживают pushState
или даже JavaScript, в зависимости от семантики вашей разметки, вы можете с ними покончить.
На момент написания статьи были проблемы с тем, как iOS обрабатывает реальные привязки, прерывания событий и pushState
. Поэтому для простоты я разработал обходной путь: я буду полагаться на атрибут HTML5 с именем data-href
, затем выполню live
вызов jQuery для каждого якоря или кнопки, для которых он установлен, и подключу вызов к Маршрутизатор на touchstart
. Это очень красиво, но реализация довольно проста:
placeAnchorHooks : function() { $('[data-href]').live('touchstart', function() { App.Router.navigate( $(this).attr('data-href'), true ); }); }
Использование touchstart
вместо click
имеет огромное значение для отзывчивости приложения. В качестве упражнения замените его на click
(сначала вам нужно будет клонировать репозиторий), чтобы увидеть, что произойдет. Вы заметите, что при каждом касании происходит заметная задержка, прежде чем экран реагирует на касание. Используя touchstart
вместо click
, мы работаем над этим.
Если вам интересно, да, мы могли бы обойтись без pushState
и вместо этого полагаться на «hashbangs», чтобы все pushState
. Я делаю это, чтобы показать, насколько просто иметь прозрачные pushState
поддержкой pushState
в вашем приложении. Это можно использовать и с не-мобильными веб-приложениями.
настройки
В этом примере мы разрешаем только одну настройку. Пользователь может указать ключевые слова, которые приложение будет использовать, чтобы сузить, какие магазины оно должно показывать. Это, однако, может быть легко расширено до ряда атрибутов. Особо следует отметить, как мы сохраняем эти предпочтения. Я переопределил метод sync
для модели «Магистрали Settings
, чтобы он сохранял атрибуты с помощью localStorage .
Это имеет смысл, когда вы понимаете, что мы сохраняем настройки для каждого устройства. И до тех пор, пока не станет нормой носить с собой более одного мобильного телефона, это подойдет. Переопределение состоит из следующего:
var Settings = Backbone.Model.extend({ sync: function(method, model, options) { switch(method) { case 'create': case 'update': localStorage.setItem('Settings', JSON.stringify(model)); break; case 'delete': localStorage.removeItem('Settings'); break; case 'read': var settings = localStorage.getItem('Settings'); model.attributes = (settings && JSON.parse(settings) || {}); return model; } return this; }
Представления являются подпрограммами
В этом примере используются два просмотра Backbone : один для домашней страницы и один для страницы настроек. Я не буду вдаваться в подробности о том, что такое Backbone Views, но думаю о них как о автономных суб-приложениях внутри вашего большого приложения. Класс HomeView
имеет базовый элемент, и все визуальные элементы, связанные с ним, происходят в этом элементе. Он также следит за изменениями в модели Settings
с помощью этой строки:
App.Settings.bind('change', this.hideMenu, this);
Поэтому мы перестаем приставать к этому подпрыгивающему восклицательному знаку, как только он установит несколько ключевых слов.
Класс SettingsView
в свою очередь, имеет два задания. Во-первых, он берет то, что уже есть в модели Settings
(скажем, вы уже установили ключевые слова), и заполняет им текстовое поле, чтобы пользователь мог видеть, что там. Во-вторых, он захватывает то, что находится в текстовом поле, и помещает это в модель Settings
когда мы изменяем значение, отправляя форму.
Пользовательские страницы
Было бы интересно позволить владельцам магазинов настраивать внешний вид своего мобильного магазина. Но только до некоторой степени, потому что мы не хотим нанести еще один MySpace миру. Итак, мы позволим им выбирать темы, которые состоят из нескольких переопределений, которые добавляются через класс CSS. К счастью, это действительно легко сделать.
body > section.coffee { background: -webkit-gradient(radial, 50% center, 200, 50% center, 40, from(#a54e03), to(#bc7940)), #a54e03; } body > section.coffee h1, body > section.coffee p { text-shadow: 1px 1px 0 #000 }
Тема «кофе» — это просто вариант оригинальной красной темы, похожей на кофейный. В качестве бонуса я добавил тени для текста, чтобы улучшить читаемость, поскольку светло-коричневый цвет обеспечивает намного меньшую контрастность, чем оригинальный темно-красный, при рассмотрении белого текста. Остальные темы примера — все варианты этого.
Допустим, владелец магазина при настройке своего магазина выбрал тему «кофе». В этом примере у нас есть несколько жестко закодированных ссылок в HTML, пара рабочих магазинов и только одна с небольшим содержанием. мы загружали их с сервера через Ajax, мы загружали настройки магазина, определяли выбор темы и применяли соответствующий класс CSS к элементу <section>
.
В двух словах, это рецепт для настройки страниц в нашей небольшой фреймворк.
обоснование
Вы можете быть удивлены, почему вы проходите через все это, когда бросаете фреймворк, это звучит намного проще и быстрее. Идея здесь двоякая: чтобы вы поняли, что создать это с нуля очень просто, и дать вам базовый шаблон, который вы можете сохранить, чтобы вы всегда могли запустить новое приложение из него.
Вы можете либо макросировать содержимое этих файлов в текстовом редакторе, либо написать скрипт и создать мини-набор инструментов, или даже вручную скопировать этот шаблон в новую папку. Как только вы начнете это делать, вы загрузите новое приложение так же быстро, как и с любой другой платформой.
Производительность
Некоторые из вас заметят, что сценарии JavaScripts несжатые, каждый разделен на собственный файл. Сохранять это в производстве — ужасная идея, особенно если мы говорим о мобильных приложениях, где задержка намного дороже. Если вы развернете это как приложение, убедитесь, что вы минимизировали и связали свои сценарии, так как это значительно ускорит процесс.
Другим важным соображением, в том же духе минимизации количества HTTP-запросов, которые должен сделать мобильный браузер для загрузки вашего приложения, является обеспечение того, чтобы любые изображения, составляющие часть приложения, например стрелки для кнопок, логотип приложения и т. Д. на, либо:
- Встраивается в CSS с использованием Data URI . Это немного увеличит размер изображения, но вы все равно выиграете. Чем меньше HTTP-запросов нужно сделать вашему приложению, тем быстрее оно будет.
- Конвертируется в SVG и встраивается в HTML. Это мой личный фаворит. iOS 4 и 5 и Android Honeycomb все поддерживают SVG, поэтому, чтобы оставаться в безопасности, если вы ожидаете, что вам придется поддерживать браузеры, не поддерживающие SVG, перейдите к предыдущему варианту.
Прощальные слова
Риск разжигания беспорядка, позвольте мне прояснить одну вещь: в мобильных платформах нет ничего принципиального . Ну, некоторые из них очень плохие, но в целом большинство из них предлагают поддержку старых, безликих мобильных браузеров. Если вам нужно поддерживать эти браузеры, то обязательно сделайте каркас, так как это, безусловно, сэкономит ваше время.
Однако, если вы хотите, чтобы ваше приложение работало на Android или iOS, примите ограничения. Есть несколько ограничений, и они в основном ориентированы на дизайн, поэтому используйте свой собственный каркас, используя минималистский целевой подход с Backbone.js.