Но к счастью, этот топик не будет наполнен обыденной трагедией разработки, подобных приложений. Поскольку сегодня, я покажу на реальном примере, как разрабатывать приложения под Firefox OS, которая поддерживает большую часть современных веб-технологий, и вообще говоря создана для них и благодаря им.
Рецепт
Для приготовления нам понадобиться:
0) Апи какого-нибудь сервиса, в нашем случае это ФотоФания (не сочтите за рекламу).
1) Angular js — в качестве основы
2) buildingfirefoxos.com/building-blocks/ — заготовки ui-блоков
3) jQuery и прочие либы по вкусу
0) Апи
Поскольку хочется показать реальное приложение, то и апи надо использовать реальное. ФотоФания это сайт с помощью которого из своих скучных сэлфи можно создать веселые фоточки. Так что нам, немного, придется работать с изображениями на клиенте (хотя основная работа будет происходить на сервере, разумеется).
1) Angular js
Angular хорош. Конечно у меня есть притенении к нему, с точки зрения производительности на мобильных девайсах, но он позволяет отстранится от рутинного кода, обновлений элементов и прочего, занимаясь только данными. Кроме того, приложение на Angular без особых усилий получается отлично структурированным.
2) Building Blocks
Building Blocks — это готовые элементы дизайна, взятые (частично) из исходников Gaia — UI прослойки Firefox OS. Эти заготовки не используют js, вся логика, которая там есть, реализована через псевдо-селекторы. При этом, весь код написан с помощью html5 элементов, и использует data-* атрибуты для обозначения роли блока.
Еще я заметил важную вещь — код Building Blocks работает быстрее и плавнее чем код, который я написал сам, как бы я не старался. По крайней мере, это касается списков. Возможно определённые элементы ускоряются нативно — не уверен насколько это правда. Так что по возможности пользуйтесь заготовками — они ускоряют разработку, делают приложение похожим на нативное и делают его плавнее.
3) jQuery и прочие
Вообще говоря, я бы с радостью избавился от jQuery, но он является зависимостью плагина для пинч-зума github.com/segdeha/jquery-pan-zoom. Подсадить плагин на Zepto не получилось. К счастью jQuery можно билдить из тех кусков которые вам нужны. Поэтому я беспощадно избавился от SIzzle и прочих ненужных в быту вещей.
Немного о структуре
Структуру приложения каждый использует какую хочет. В моем случае она похожа на
/build/ /build.js /build.css /scripts/ /vendors/ /controllers/ /services/ /.../ /app.js /dictionary.js /config.js /styles/ /helpers/ /variables.less /mixins.less /main.less /header.less /... /images/ index.html
Скрипты и стили билдятся с помощью grunt в реальном времени. А grunt release уже все минифицирует. Так же я использую grunt-angular-templates который все шаблоны превращает в js код, который так же добавляется в build.js
Первоначальная разметка страницы
<!doctype html> <html ng-app="PhotoFunia" ng-csp> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta charset="UTF-8"> <title>PhotoFunia</title> <link rel="stylesheet" href="build/build.css"/> </head> <body role="application"> <!-- Прелоадер, который показывается при старте. Я не выношу его в отдельный файл, в надежде что так будет быстрее--> <section ng-controller="PreloaderController"> <div id="preloader" ng-hide="preloaderHide"> <div class="preloader-content"> <div class="logo"></div> </div> </div> </section> <!-- Это наш дроуэр (меню) которое будет выдвигаться слева --> <section data-type="sidebar" ng-include="VIEWS.DRAWER"></section> <!-- Это наш контент. Не знаю почему у него id="drower" так нам сказал building blocks --> <section id="drawer" role="region" ng-view ng-class="{'menu-opened': drawerOpened}"></section> <!-- Это своеобраный toast для FF OS. http://buildingfirefoxos.com/building-blocks/status.html --> <section role="status" ng-controller="ToastController"> <p ng-if="text" ng-bind="text"></p> </section> <!-- Тут скрывается универсальный код для попапов --> <section ng-include="VIEWS.POPUP"></section> <script src="build/build.js"></script> </body> </html>
Важно
Думаю некоторые заметили ng-csp в элементе. Эта директива включает поддержку Content Security Policy — и это является обязательным, для создания приложения для Firefox OS. Кроме того, нам надо немного пропатчить сам angular!
Ищем в коде angular строчку window.XMLHttpRequest();
и заменяем на window.XMLHttpRequest({mozSystem: true});
А так же нам надо добавить руками в наши стили — вспомогательные стили angular.js. А именно — code.angularjs.org/1.2.16/angular-csp.css
Немного о js
Надо заметить, что я изначально пишу код на coffeescript, но буду вам показывать его javascript аналог. Возможно где-то всплывут неточности — тогда мы вместе их исправим.
Еще я надеюсь, что вы знакомы с angular и js достаточно хорошо, чтобы мне не останавливаться на каждом шагу. Иначе эту статью никто никогда в жизни не дочитает.
Мультиязычность
Наше приложение будет мультиязычным. Поэтому нам требуется словарь, его интерпретатор и какая-нибудь директива или фильтр, чтобы вставлять слова в код. Часто когда речь заходит об мультиязычности с помощью angular — всплывают какие-то фильтры, которые выглядят не очень приятно. Например нам предлагают писать так <div ng-bind="'header_search_title' | l10n"></div>
меня это немного корежит — зачем вызывать каждый раз лишнюю функцию и писать такой неудобный код, если можно просто использовать словарь в глобал-скоупе, и обращаться к нему? Например так <div ng-bind="m.header.search.title"></div>
. За всё время моей работы, такой способ меня не подводил. Давайте же его и реализуем.
Хочется чтобы он был любой вложенностью, поддерживал любое количество языков. Без проблем!
var DICTIONARY = { header: { search: { title: { ru: 'Поиск', en: 'Search' }, favorites: { ru: 'Избранное', en: 'Favorites' } } }, cancel: { ru: 'Отмена', en: 'Cancel' } };
Структура есть, теперь, чтобы не обращаться напрямую переменной (а не языку), надо написать небольшой парсер.
App.run(['$rootScope', function ($rootScope) { var lang = 'ru'; // тут должна быть какая-то логика :) $rootScope.m = (function() { var parse = function(obj, result) { result = result || {}; for (var key in obj) { var value = obj[key]; if (typeof value !== "object") return obj[lang]; result[key] = parse(value); } return result; }; return parse(DICTIONARY); })(); }]);
То есть мы просто рекурсивно доходим, до того момента, когда значение переменной становится не объектом, и возвращаем значение с нужным языком. Все просто, и теперь у нас есть переменная m в рут-скоупе, к которой мы можем смело обращаться.
Кто-то наверняка засомневается в производительности такого решения, но могу вас уверить, что это капля в море. И вообще вам никто не мешает написать директиву, которая не будет вешать ватчер на переменную. Так что все в порядке.
Вернемся к разметке
Давайте доделаем drawer (Меню). Допустим у нас есть уже основа в виде js. Есть контроллер который обслуживает наше меню, и мы уже получили с помощью апи список категорий, которые будем выводить в меню. Тогда код меню получается очень простым.
Как видно, в своей разметке index.html мы уже использовали часть кода из Building Blocks. Сейчас мы возьмем код Drawer’a и немного изменим под себя. buildingfirefoxos.com/building-blocks/drawer.html
<nav ng-controller="DrawerController"> <div class="empty-space"></div> <ul> <li> <a ng-click="go('/favorites'); closeDrawer()"> <span class="text" ng-bind="m.menu.favorite"></span> <span class="counter" ng-bind="favorite.get().length"></span> </a> </li> </ul> <h2 ng-bind="m.category.title"></h2> <ul> <li ng-repeat="cat in categories"> <a ng-click="openCategory(cat.key)"> <span class="text" ng-bind="cat.title"></span> <span class="counter" ng-bind="cat.count"></span> <span class="new-counter" ng-if="cat.new_count" ng-bind="'+'+cat.new_count"></span> </a> </li> </ul> </nav>
Как видно у нас два списка в меню, в самом верхнем лежит только одна ссылка на избранное (заметили что мы уже используем нашу переменную m?), там же выводится кол-во избранных фотоэффектов, сбоку. Ниже у нас, собственно, сам список категорий.
И для полноты картины сделаем еще одну страницу — список эффектов, то есть страница категории.
buildingfirefoxos.com/building-blocks/headers.html — тут берем хедеры
buildingfirefoxos.com/building-blocks/lists.html — тут списки
buildingfirefoxos.com/building-blocks/filters.html — тут фильтры (табы)
buildingfirefoxos.com/building-blocks/buttons.html — а тут кнопочи
И получаем, упрощенно, что-то такое:
<!-- Наш хедер, собсвенной персоной --> <header> <menu type="toolbar"> <!-- Кнопочка для поиска --> <a ng-click="go('/search')"> <span class="icon action-icon search"></span> </a> </menu> <!-- Две кнопки для открытия / закрытия меню (drawer) --> <a ng-click="closeDrawer()"><span class="icon icon-menu"></span></a> <a ng-click="openDrawer()"><span class="icon icon-menu"></span></a> <h1 ng-bind="category.title"></h1> </header> <div role="main" data-type="list" id="category"> <!-- Табы, которые устанавливают сортировку эффектов --> <ul role="tablist" data-type="filter" data-items="2"> <li role="tab" aria-selected="{{ sorting === 'new'}}"> <a ng-click="setSorting('new')" text="m.category.new"></a> </li> <li role="tab" aria-selected="{{ sorting === 'popular'}}"> <a ng-click="setSorting('popular')" text="m.category.popular"></a> </li> </ul> <!-- И наши эффекты собственной персоной --> <ul class="effects"> <li ng-repeat="effect in effects" ng-click="openEffect(effect.key)"> <aside class="pack-end"> <img ng-src="{{CONFIG.DOMAIN + effect.icon}}"> </aside> <a> <p ng-bind="effect.title"></p> <p ng-if="effect.labels"> <span ng-repeat="label in effect.labels" ng-class="label" class="label"></span> </p> </a> </li> </ul> <!-- Кнопочка для подгрузки эффектов --> <div class="load-more" ng-if="isNeedMore && effects.length"> <button ng-click="showMore()" text="m.category.show_more"></button> </div> </div>
Надеюсь все понятно. А понять надо следущее — мы берем готовые блоки и вырезаем из них самое нужное нам. Ими можно крутить и вертеть как угодно.
Таким же образом делаются все остальные страницы.
Об API Firefox OS
В приложениях Firefox OS версии >= 1.3 input[file] работать не будет, в замен его, нам предлагают пользоваться MozActivity. Что даже упрощает работу.
Вот так выглядит получение картинки:
var pick = new MozActivity({ name: "pick", data: { type: ["image/*"], nocrop: true } }); pick.onsuccess = function() { var blob = pick.result.blob; // что-то делаем с блобом };
Вот так шеринг:
new MozActivity({ name: "share", data: { type: "image/*", number: 1, blobs: [blob] } });
И примерно так — сохранение:
var sdcard = navigator.getDeviceStorage("sdcard"); var request = sdcard.addNamed(blob, name); request.onsuccess = function() {}; request.onerror = function() {};
После того, как приложение готово, его надо сбилдить и протестировать.
Чтобы билдить — можно использовать эмуляторы.
Я вам предлагаю использовать Менеджер приложений, который встроен по умолчанию в новые версии firefox. developer.mozilla.org/ru/docs/Mozilla/Firefox_OS/Using_the_App_Manager. Тут же можно устанавливать разные версии эмуляторов, подключать девайсы и дебажить! Что очень удобно.
Так же нам нужен манифест приложения. Выглядит и пишется он очень просто.
Приведу пример:
{ "name": "PhotoFunia", "description": "PhotoFunia is the best way to add a spark to your photos, make them special and more original.", "launch_path": "/index.html", "version" : "1", "type": "privileged", "developer": { "name": "", "email" : "" }, "default_locale": "en", "icons": { "256": "/images/app_icon_256.png" }, "permissions": { "device-storage:pictures": { "description": "Save result images", "access": "readwrite" }, "device-storage:sdcard": { "description": "Save result images", "access": "readwrite" }, "mobilenetwork": { "description": "Check for available connection" }, "systemXHR": { "description": "Need for internet connection" } } }
Собственно говоря, засовываем все нужные файлы в .zip архив и отправляем в marketplace.firefox.com на проверку. Через пару дней (а может и к вечеру) приложение появится в маркете!
Что это было?
Я хотел донести до вас, что разработка под Firefox OS это очень просто, интересно и весело! Конечно, пока что, на этом денег не заработать. Но можно подумать, мы здесь ради денег собрались. Так что, берите IDE в руки, и создавайте приложения под Firefox OS!
Тем, лучшим из нас, у кого есть девайс на Firefox OS — прошу к столу marketplace.firefox.com/app/photofunia/
Все пожелания, а так же ошибки и недочеты принимаю в личку.
ссылка на оригинал статьи http://habrahabr.ru/company/surfo/blog/218819/
Добавить комментарий