Pull to refresh

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

Reading time7 min
Views15K
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 и работу с ещё кое-чем интересным. Если у вас появились трудности, вопросы или предложения для следующих статей — милости прошу в ЛС!
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+10
Comments20

Articles

Change theme settings