2015年11月25日

Ionic Framework 教學 - 10. 使用 Cordova Plugin




上一章

我們來試著製作一個會用到相機的 App 來練習 Plugin 的使用,相機的功能是藉由 Cordova Plugin 來連接的。


首先 Shell 指令進入我們的 project,然後增加手機目標平台系統,ios 或是 android
cd mysidemenu
ionic platform add android
ionic platform add ios

然後安裝 Camera Plugin 和 Canvas2Image Plugin
npm update -g cordova
cordova platform update android@5.0.0
cordova plugin add cordova-plugin-camera
cordova plugin add https://github.com/devgeeks/Canvas2ImagePlugin.git
cordova plugin add cordova-plugin-whitelist

在 www/templates/menu.html 增加一個選單
<ion-item menu-close href="#/app/photo">
  Wanted
</ion-item>

然後增加 www/templates/photo.html
<ion-view>
  
  <!-- add a camera button to take a photo -->
  <ion-nav-buttons side="secondary">
    <button
      ng-click="capturePhoto()"
      class="button button-icon icon icon-small ion-camera">
    </button>
  </ion-nav-buttons>

  <ion-content>

    <div
      style="
        width:320px;
        height:453px;
        overflow: hidden;
        margin: 0 auto;
      ">
      <div
        style="
          width:320px;
          height: 1000px;
          background-image:url('{{photo.url}}');
          background-repeat: no-repeat;
          background-size: contain;
        ">
        <img ng-src="{{framePhoto}}"
          style="
            width:320px;
          ">
      </div>
    </div>
    <button class="button button-full" ng-click="save()">Save</button>
  </ion-content>

</ion-view>

這裡可以看到我們使用一個 background image ,圖片位置是一個變數 photo.url ,然後前面放一個圖片 img ,還有兩個按鈕對影兩個函式, capturePhoto 用來拍照, save() 用來儲存照片。
接下來我們定義一個 Controller 來處理這些邏輯。
.controller('PhotoCtrl', function($scope) {
  
  $scope.framePhoto = 'http://i.imgur.com/5WkxqFC.png?1';
  //photo to show in view page
  $scope.photo = {};
  
  //options to take a photo
  var photoOptions = {};
  if( typeof Camera !== 'undefined'){

    //https://github.com/apache/cordova-plugin-camera
    photoOptions = {
      quality: 100,
      destinationType: Camera.DestinationType.FILE_URL,
      sourceType: Camera.PictureSourceType.CAMERA, //[PHOTOLIBRARY, CAMERA, SAVEDPHOTOALBUM]
      encodingType: 1, //jpeg:0 png:1
      targetWidth: 320,
      targetHeight: 453
    };

  }

  /**
   * Capture a photo, when user click the camera icon
   */
  $scope.capturePhoto = function() {
    if(navigator.camera){
      navigator.camera.getPicture(
        onGetPictureSuccess, onGetPictureError, photoOptions);
    }
  };

  /**
   * Called when the photo is taken
   * @param  {string} result photo local url
   */
  function onGetPictureSuccess (result) {
    console.log('onGetPictureSuccess', result);
    $scope.photo.url = result;
    $scope.$apply();
  }

  /**
   * Called when the capture photo runs into an error
   * @param  {object} err [description]
   */
  function onGetPictureError (err) {
    console.error('error', err);
  }



  /**
   * Load an image from a file path, call 'onload' function when loaded.
   * @param  {string} src    image file path
   * @param  {function} onload callback function
   * @return {image}        
   */
  function loadImage(src, onload) {
    console.log('loadImage');
    var img = new Image();

    img.onload = onload;
    img.src = src;
    return img;
  }

  
  /**
   * Combines two images in a canvas, use the size of image 1.
   * @param  {string}   src1     file path of image 1
   * @param  {string}   src2     file path of image 2
   * @param  {Function} callback function when 2 images combined
   */
  function combineImages (src1, src2, callback) {
    console.log('combineImages', [src1, src2]);
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d'), dataURL;
    var img1 = loadImage(src1, onload);
    var img2 = loadImage(src2, onload);
    var i = 0, base64Photo;
    function onload() {
      i += 1;
      if(i == 2){ //when 2 images are both loaded
        i = 0;
        canvas.width = img1.width;
        canvas.height = img1.height;
        //draw img2 according to img1's ratio
        ctx.drawImage(img2, 0, 0, img2.width, img2.height, 0, 0, img1.width, img1.width*img2.height/img2.width);
        ctx.drawImage(img1, 0, 0);
        callback(canvas);
      }
    }
  }

  /**
   * save image from canvas to gallery
   * @param  {canvas} canvas
   */
  function saveImageFromCanvas(canvas){
    window.canvas2ImagePlugin.saveImageDataToLibrary(
      function(msg){
          console.log('canvas2ImagePlugin', msg);
      },
      function(err){
          console.log('canvas2ImagePlugin', err);
      },
      canvas
    );
  }

  $scope.save = function() {
    //combine 2 images 
    combineImages($scope.framePhoto, $scope.photo.url, saveImageFromCanvas);
  };

})

理解程式內容請一邊閱讀程式註解。 最後我們在 www/js/app.js 增加一組 routes
.state('app.photo', {
  url: '/photo',
  views: {
    'menuContent': {
      templateUrl: 'templates/photo.html',
      controller: 'PhotoCtrl'
    }
  }
})

試著執行這個頁面,發現不能執行,因為電腦上面沒有這些 Plugin 對應的手機程式,我們要到手機上去測試執行。
有別於之前上傳的測試方式,這次我們用到 Plugin 裡面因為包含原生的程式,我們必須要直接編譯這個 App。
請到 App Store 安裝 Xcode 或是到 Android 官網下載 Android SDK,然後安裝好並且把 Android SDK 環境變數加到系統內。
接著到命令列 Shell 編譯你需要的對應的手機系統。
ionic build ios
ionic build android

編譯沒有問題之後,如果你是使用 Android 系統,你可以接上你的手機測試,請打開你手機的 developer mode ,ios 的使用者可以跑 simulator。
ionic run ios
ionic run android

遺憾的是,ios simulator 只能檢查你編譯成功,沒有支援相機功能。
iOS 的話我們進到 Xcode 打開 mysidemenu project , File > Open... > mysidemenu/platforms/ios/mysidemenu.xcodeproj
連接手機平板裝置後開始編譯。