Ionic Framework — работа с камерой

от автора

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 и работу с ещё кое-чем интересным ;). Если у вас появились трудности, вопросы или предложения для следующих статей — милости прошу в ЛС!

ссылка на оригинал статьи http://habrahabr.ru/post/256387/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *