FullStack JS Developer
0,0
рейтинг
17 ноября 2015 в 18:11

Разработка → Ionic Framework — работа с камерой tutorial

image

По результатам опроса из первого топика продолжаю серию статей об Ionic Framework (далее IF). Сегодня поговорим о работе с камерой устройства и в дополнение рассмотрим работу с localStorage. В качестве основы возьмем приложение из прошлого топика и добавим нужную функциональность.



Добавление ngCordova


Для работы установленных плагинов необходимо добавить модуль ngCordova. Ставится он просто (для начала обновим наши инструменты, ведь прошло много времени с момента публикации первой статьи...):

sudo npm i -g cordova ionic bower
bower install ngCordova

Далее ngCordova подключается в index.html:

<script src="lib/ngCordova/dist/ng-cordova.min.js"></script>

и в зависимости приложения (app.js):

angular.module('starter', ['ionic', 'ngCordova', 'starter.controllers', 'starter.services'])

иначе паровозик не сможет… Желательно также подключить еще пару плагинов для работы с ФС устройства:

ionic plugin add cordova-plugin-file
ionic plugin add cordova-plugin-file-transfer

По идее можно было бы подключить только file-transfer, т.к. вместе с собой он также тянет и file, но иногда такой способ чреват появлением непонятных багов…

Подключение плагина для работы с камерой


Для того чтобы IF смог получить доступ к камере устройства нужно добавить в проект Cordova плагин реализующий этот функционал:

ionic plugin add cordova-plugin-camera

Плагин предоставляет единственный метод:

navigator.camera.getPicture(SuccessCallback, ErrorCallback, Options);

Где SuccessCallback и ErrorCallback это наши функции-обработчики в которых мы вольны писать все что вздумается, а вот объект Options гораздо интереснее для рассмотрения:

Настройки для камеры
  • quality (Number): Качество сохраняемого изображения, в интервале от 0 до 100, где 100 — режим без сжатия. По умолчанию значение 50.
  • destinationType(Number): Формат возвращаемого значения. По умолчанию FILEURI. Настройка определяется типами navigator.camera.DestinationType:
    Camera.DestinationType = {
          DATA_URL : 0,      // Строка в формате base64
          FILE_URI : 1,      // URI файла
          NATIVE_URI : 2     // URI пути зависящий от платформы (assets-library:// для iOS и content:// для Android)
    };
    
  • sourceType (Number): Источник изображения. По умолчанию CAMERA. Настройка определяется типами navigator.camera.PictureSourceType:
    Camera.PictureSourceType = {
          PHOTOLIBRARY : 0,
          CAMERA : 1,
          SAVEDPHOTOALBUM : 2
    };
    
  • allowEdit (Boolean): Включает режим простого редактирования перед передачей в функцию.
  • encodingType (Number): Формат файла. По умолчанию «у вас JPEG». Настройка определяется типами navigator.camera.EncodingType:
    Camera.EncodingType = {
          JPEG : 0,               // JPEG
          PNG : 1                 // PNG
    };
    
  • targetWidth (Number): Ширина. Без комментариев. Для масштабирования. Лучше использовать в связке с targetHeight с коэффициентом.
  • targetHeight (Number): Высота.
  • mediaType (Number): Тип мультимедиа контента. По умолчанию PICTURE. Работает при PictureSourceType равным PHOTOLIBRARY или SAVEDPHOTOALBUM. Настройка определяется типами nagivator.camera.MediaType:
    Camera.MediaType = {
          PICTURE: 0,    // Только изображения.
          VIDEO: 1,      // Только видео (ВСЕГДА ВОЗВРАЩАЕТ FILE_URI)
          ALLMEDIA : 2   // Все типы
    };
    
  • correctOrientation (Boolean): Поворот изображения для компенсации ориентации устройства во время съемки.
  • saveToPhotoAlbum (Boolean): Сохранение в альбом после съемки.
  • popoverOptions: iOS-only параметр определяющий местонахождение popover на iPad. Настройка определяется в CameraPopoverOptions.
  • cameraDirection (Number): Камера для использования (фронтальная или задняя). По умолчанию BACK. Настройка определяется типами navigator.camera.Direction:
    Camera.Direction = {
          BACK : 0,      // Use the back-facing camera
          FRONT : 1      // Use the front-facing camera
    };
    


Вот в принципе и все что можно рассказать…


Создание сервиса для работы с камерой


Давайте для нашего удобства сделаем службу чтобы работа с камерой была комфортной. Для этого предлагаю реализовать фабрику с объектом promise который будет обрабатывать события камеры с помощью наших callback'ов. Откройте файл services.js (в прошлом топике мы уже сделали там фабрику городов) и приведите его к такому виду:

services.js
angular.module('starter.services', [])
.factory('Cities', function() {
    // ...
}).factory('Cam', function($q) {
  return {
    getPic: function(opt) {
      var q = $q.defer();
      navigator.camera.getPicture(function(res) {
        q.resolve(res);
      }, function(err) {
        q.reject(err);
      }, opt);
      return q.promise;
    }
  };
});


Итак, в этой настройке мы, используя Angular'овский $q.defer(), возвращаем promise, которому впоследствии передадим обработчики событий с камеры (SuccessCallback, ErrorCallback) и объект настройки (Options). Не закрывайте файл, мы к нему еще вернемся!

Редактирование шаблонов


Откройте файл tabs.html и добавьте еще один tab. У меня получился такой:

  <ion-tab title="Options" icon-off="ion-gear-a" icon-on="ion-gear-a" href="#/tab/opt">
    <ion-nav-view name="tab-opt"></ion-nav-view>
  </ion-tab>

Теперь в папке с шаблонами создадим еще один шаблон (например tab-opt.html) и сделаем небольшую форму настройки пользователя приложения, например такую:

tab-opt.html
<ion-view view-title="Настройки">
	<ion-content class="padding">
		<div class="list-item">
			<img class="full-image" ng-src="{{user.ava || './img/ionic.png'}}">
			<label class="item item-input">
				<i class="icon ion-person placeholder-icon"></i>
				<input type="text" placeholder="Имя пользователя" ng-model="user.name" ng-keyup="setUser();" />
			</label>
			<label class="item item-input item-select">
				<div class="input-label">
					Город
				</div>
				<select ng-model="user.city" ng-change="setUser();">
					<option ng-repeat="city in cities" ng-value="city.id">{{city.name}}</option>
				</select>
			</label>
			<button class="button button-full button-positive" ng-click="getPic(1);">
				С камеры
			</button>
			<button class="button button-full button-calm" ng-click="getPic(0);">
				Из библиотеки
			</button>
		</div>
	</ion-content>
</ion-view>


Как видно, к полям мы привязали модель пользователя и при изменении значения поля html, в модели будет изменяться соответствующее поле объекта. Так же на события когда клавиша отпущена (кстати, тут можно использовать debounce) и на изменение выбора города, была сделана привязка обработчика setUser, это все мы опишем на следующем шаге. Не терпится запустить и посмотреть? Но мы еще не настроили маршрутизацию, контроллеры и работу с localStorage, так что придется еще немного подождать…

Добавление маршрутов, контроллеров и фабрик


Займемся для начала маршрутами. Откройте app.js и добавьте маршрутизацию для нашего нового таба:

Подсказка
.state('tab.opt', {  
    url: '/opt',
    views: {
      'tab-opt': {
        templateUrl: 'templates/tab-opt.html',
        controller: 'OptCtrl'
      }
    }
})


Отлично, теперь перейдем в controllers.js и добавим нужный контроллер:

OptCtrl
.controller('OptCtrl', function($scope, $ionicPopup, Cities, Cam, LS) {
	$scope.cities = Cities.all();
	$scope.user = LS.getIt('user') || {};
	$scope.showAlert = function(title, text) {
		$ionicPopup.alert({
			title: title,
			template: text
		});
	};
	$scope.setUser = function() {
		LS.setIt('user', $scope.user);
	};
	$scope.getPic = function(source) {
		var opt = {
			sourceType: source
		};
		Cam.getPic(opt).then(function(res) {
			$scope.user.ava = res;
			$scope.setUser();
		}, function(err) {
			$scope.showAlert("Ошибка", err);
		});
	}
})


Опа, что за LS в зависимостях контроллера!? Ничего не бойтесь, это всего лишь метод для работы с localStorage, и настало время его реализовать (services.js):

LS
.factory('LS', function() {
  return {
    getIt: function(item) {
      var it = false;
      try {
        it = JSON.parse(localStorage.getItem(item));
        return it;
      } catch(e) {
        console.err(e);
        return it;
      }
    },
    setIt: function(item, obj) {
      try {
        localStorage.setItem(item, JSON.stringify(obj));
        return true;
      } catch(e) {
        console.err(e);
        return false;
      }
    }
  };
})


И надо бы добавить еще городов, добавьте свой:

{
    id: 555312,
    name: 'Иваново',
    desc: 'Город невест',
    emblem: 'https://upload.wikimedia.org/wikipedia/commons/8/8f/Coat_of_Arms_of_Ivanovo_oblast.png'
}

Вы сейчас наверное думаете «к чему столько телодвижений?». А представьте себе, что пользователь настроил город (автоматически сохранив в localStorage) и при повторном запуске приложения его будет сразу перекидывать на погоду в родном городе, круто же! Финальный штрих, изменим два наших контроллера, написанных в прошлой статье:

CityCtrl, CityDetailCtrl
.controller('CityCtrl', function($scope, Cities, LS) {
	$scope.cities = Cities.all();
	$scope.user = LS.getIt('user') || {};
	if ($scope.user.city) { window.location.href = '#/tab/city/'+$scope.user.city; }
})
.controller('CityDetailCtrl', function($scope, $http, $stateParams, $ionicPopup, $ionicLoading) {
	$scope.data = {};
	$scope.id = $stateParams.id;
	$scope.showAlert = function(title, text) {
		$ionicPopup.alert({
			title: title,
			template: text
		});
	};
	$scope.refresh = function() {
		$ionicLoading.show();
		$http.get('http://api.openweathermap.org/data/2.5/forecast/daily?id='+$scope.id+'&appid=2de143494c0b295cca9337e1e96b00e0')
		.success(function(data, status, headers, config) {
			$scope.data = data;
			$ionicLoading.hide();
			$scope.$broadcast('scroll.refreshComplete');
		})
		.error(function(data, status, headers, config) {
			$ionicLoading.hide();
			$scope.showAlert(status, data);
			$scope.$broadcast('scroll.refreshComplete');
		});
	};
	$scope.refresh();
})


Мы добавили условие, чтобы пользователя перекидывало на настроенный город, а также незаметно указали в зависимостях компонент $ionicLoading для показа окна ожидания во время запроса. Надеюсь ничего не пропустил, все готово, вы знаете что делать (ionic serve --lab, если забыли):

It works!



Заключение


В принципе на текущий момент работать с помощью IF становится всё удобнее и проще, поэтому я смело могу сказать что это один из лучших фреймворков для прототипирования, и даже для production приложений. Дерзайте! В следующей статье хочу описать создание чата с помощью socket.io и работу с ещё кое-чем интересным. Если у вас появились трудности, вопросы или предложения для следующих статей — милости прошу в ЛС!
Артем Комаров @m0sk1t
карма
24,5
рейтинг 0,0
FullStack JS Developer
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (20)

  • –1
    А чем он лучше, скажем, Titanium?
    • 0
      Не знаю, так и не удалось запустить Titanium с полгода-год назад…
      • –1
        Я вам скажу чем хуже. Прежде всего, он не умеет работать с интентами, сервисами, создавать активити, меню, а так же не знает ничего о системных единицах измерений (dp, dpi, sp)
        • 0
          Знайте что я, как и любой другой пользователь хабра, с удовольствием бы прочитал введение в Titanium аналогичное моему по IF. Не затруднит ли вас написать tutorial? Интересно узнать как JS общается с Native элементами!

          P.S. Прочитав тут 30 статей про Titanium я, к сожалению, увидел много боли в постах и комментариях… Например тут
          • +1
            Конечно не затруднит, я как раз систематизирую свои знания по нему, однако не могу обнадежить вас по поводу сроков.
            В приведенной ссылке сравнивается большое с холодным, ведь js-фреймворк ни за что не сравнится с нативной разработкой; кроме того, многие отрицательные моменты оттуда уже не актуальны:
            * interface builder есть давно, это alloy, и там есть даже data binding
            * цвет placeholder'ов имеется в 5 релизе
            * память после уничтожения activity освобождается в том же 5 релизе

            мне самому был очень интересен IF еще с первого релиза, и у них есть много классных фич вроде удаленной сборки ipa без мака, ionic push/ionic creator, но вот реально, это все для разработчика хорошо, а надо чтобы было хорошо для клиента
            • +1
              Грустно, что за титаниум надо платить. Сперва платим за титаниум, потом за гугл, потом за ios.
              Плюс ещё надо тратить деньги на обучение сотрудников тому же титаниуму(хоть там и js, но это же не стандартные функции). Чего нет в IF, где Angular един, а IF выступает мобильным «бутстрапом» с кучей примеров, тулзов, фич и коммюнити.
              • +1
                не надо если не хотите аналитикой пользоваться. сам SDK бесплатный
                а обучение ангуляру стоит тех же самых временных и, как следствие, финансовых затрат
                • +1
                  Angular продвигает сам большой брат.
                  • 0
                    Но это же не аргумент. До сих пор я ни в одном гибридном фреймворке, за исключением IF, его не встречал
            • +1
              А я вот буквально сегодня выбирал между IF и Phonegap + Onsen UI. И мне бы хотелось сравнение этих фреймворков. Как я понял, по факту это форки Кордовы, просто IF реализует ещё некоторые контроллы? Остановился я на Phonegap. Не смог найти инфу про удалённую сборку и WP в IF.
              • 0
                Скорее не форки а обертки, ибо можно писать как

                cordova plugin add
                

                так и

                ionic plugin add
                

                Эффект тот же. Просто у IF немного другая архитектура JS проекта, шаблоны и т.д. (Angular же)
                Windows Phone and FirefoxOS support is on our roadmap.

                Т.е. в скором времени можно ожидать)
        • +1
          cordova всё это умеет и даже большее
  • +1
    С камерой главное помнить — не надо выводить в консоль base64 фотки, при этом либо нет ошибки и ничего не происходит, либо все зависает насмерть
    • 0
      это да, но здесь base64 скорее в качестве примера, нежели боевой код)
  • 0
    Друг мой, продолжайте писать! Тема IF очень интересна.
    • 0
      okay)
  • +1
    Полгода назад пробовали писать приложение на IF. Получилось не очень. Лаги андроида даже с Crosswalk не побороли. На iOS заметно подлагивали переходы между $state (хотя тут мы грешим на частое использование SVG) и не работали пуши в iOS8. На WinPhone и вовсе было слайдшоу. Все-таки Ангуляр тяжеловат для мобил.
    • 0
      Лаги и баги, к сожалению — вечные спутники гибридных приложений… Поэтому я и акцентировал внимание на то что IF прекрасно подойдёт для прототипирования)
    • +1
      Последние версии IF на последних мобилках ведут себя очень вкусно. Но стоит пересесть на тот же iphone 4 как всё вдруг становится грустным.
      IF привлекает в первую очередь скоростью и простотой разработки, а также боевым комплектом готового стафа(+поддержка cordova конечно же).
      Я бы предложил вместо IF посмотреть в сторону несколько более сложных фреймворков, которые делают нативный гибрид. Например, nativescript и его аналоги.
      Конечно, на базе таких представителей не сделать приложение с суперкрутым дизайном, но для типовых проектов/прототипов/первых версий пойдет на ура.
      • +1
        или AppGyver Steroids

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.