Skip to content

Commit

Permalink
feat(web): add file input method for camera (#1856)
Browse files Browse the repository at this point in the history
Co-authored-by: jcesarmobile <[email protected]>
Co-authored-by: Daniel Imhoff <[email protected]>
  • Loading branch information
3 people authored Jul 7, 2020
1 parent 8241c81 commit 25505d2
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 22 deletions.
16 changes: 14 additions & 2 deletions core/src/core-plugin-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ export interface CameraOptions {
*/
source?: CameraSource;
/**
* iOS only: The default camera direction. By default the rear camera.
* iOS and Web only: The camera direction.
* Default: CameraDirection.Rear
*/
direction?: CameraDirection;
Expand All @@ -344,6 +344,15 @@ export interface CameraOptions {
*/
presentationStyle?: 'fullscreen' | 'popover';

/**
* Web only: Whether to use the PWA Element experience or file input. The
* default is to use PWA Elements if installed and fall back to file input.
* To always use file input, set this to `true`.
*
* Learn more about PWA Elements: https://capacitorjs.com/docs/pwa-elements
*/
webUseInput?: boolean;

/**
* If use CameraSource.Prompt only, can change Prompt label.
* default:
Expand Down Expand Up @@ -393,7 +402,10 @@ export interface CameraPhoto {
*/
exif?: any;
/**
* The format of the image. Currently, only "jpeg" is supported.
* The format of the image, ex: jpeg, png, gif.
*
* iOS and Android only support jpeg.
* Web supports jpeg and png. gif is only supported if using file input.
*/
format: string;
}
Expand Down
117 changes: 99 additions & 18 deletions core/src/web/camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
CameraPlugin,
CameraPhoto,
CameraOptions,
CameraResultType
CameraResultType,
CameraDirection,
CameraSource
} from '../core-plugin-definitions';

export class CameraPluginWeb extends WebPlugin implements CameraPlugin {
Expand All @@ -16,29 +18,108 @@ export class CameraPluginWeb extends WebPlugin implements CameraPlugin {
}

async getPhoto(options: CameraOptions): Promise<CameraPhoto> {
options;

return new Promise<CameraPhoto>(async (resolve, reject) => {
const cameraModal: any = document.createElement('pwa-camera-modal');
document.body.appendChild(cameraModal);
await cameraModal.componentOnReady();
cameraModal.addEventListener('onPhoto', async (e: any) => {
const photo = e.detail;

if (photo === null) {
reject('User cancelled photos app');
} else if (photo instanceof Error) {
reject(photo.message);
if (options.webUseInput) {
this.fileInputExperience(options, resolve);
} else {
if (customElements.get('pwa-camera-modal')) {
const cameraModal: any = document.createElement('pwa-camera-modal');
document.body.appendChild(cameraModal);
try {
await cameraModal.componentOnReady();
cameraModal.addEventListener('onPhoto', async (e: any) => {
const photo = e.detail;

if (photo === null) {
reject('User cancelled photos app');
} else if (photo instanceof Error) {
reject(photo.message);
} else {
resolve(await this._getCameraPhoto(photo, options));
}

cameraModal.dismiss();
document.body.removeChild(cameraModal);
});

cameraModal.present();
} catch (e) {
this.fileInputExperience(options, resolve);
}
} else {
resolve(await this._getCameraPhoto(photo, options));
console.error(`Unable to load PWA Element 'pwa-camera-modal'. See the docs: https://capacitorjs.com/docs/pwa-elements.`);
this.fileInputExperience(options, resolve);
}
}
});
}

private fileInputExperience(options: CameraOptions, resolve: any) {
let input = document.querySelector('#_capacitor-camera-input') as HTMLInputElement;

const cleanup = () => {
input.parentNode && input.parentNode.removeChild(input);
};

cameraModal.dismiss();
document.body.removeChild(cameraModal);
});
if (!input) {
input = document.createElement('input') as HTMLInputElement;
input.id = '_capacitor-camera-input';
input.type = 'file';
document.body.appendChild(input);
}

cameraModal.present();
input.accept = 'image/*';
(input as any).capture = true;

if (options.source === CameraSource.Photos || options.source === CameraSource.Prompt) {
input.removeAttribute('capture');
} else if (options.direction === CameraDirection.Front) {
(input as any).capture = 'user';
} else if (options.direction === CameraDirection.Rear) {
(input as any).capture = 'environment';
}

input.addEventListener('change', (_e: any) => {
const file = input.files[0];
let format = 'jpeg';

if (file.type === 'image/png') {
format = 'png';
} else if (file.type === 'image/gif') {
format = 'gif';
}

if (options.resultType === CameraResultType.DataUrl || options.resultType === CameraResultType.Base64) {
const reader = new FileReader();

reader.addEventListener('load', () => {
if (options.resultType === CameraResultType.DataUrl) {
resolve({
dataUrl: reader.result,
format
} as CameraPhoto);
} else if (options.resultType === CameraResultType.Base64) {
const b64 = (reader.result as string).split(',')[1];
resolve({
base64String: b64,
format
} as CameraPhoto);
}

cleanup();
});

reader.readAsDataURL(file);
} else {
resolve({
webPath: URL.createObjectURL(file),
format: format
});
cleanup();
}
});

input.click();
}

private _getCameraPhoto(photo: Blob, options: CameraOptions) {
Expand Down
5 changes: 4 additions & 1 deletion example/src/pages/camera/camera.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@

<ion-content padding>
<img [src]="image" />
<button (click)="getPhoto()" ion-button color="primary">Prompt</button>
<button (click)="getPhotoPWAElements()" ion-button color="primary">Prompt (PWA Elements)</button>
<button (click)="getPhotoFront()" ion-button color="primary">From Camera (front facing)</button>
<button (click)="takePicture()" ion-button color="primary">From Camera</button>
<button (click)="getFileInput()" ion-button color="primary">File Input</button>
<button (click)="getFromPhotos()" ion-button color="primary">From Photos</button>
<button (click)="getBase64()" ion-button color="primary">Prompt (b64)</button>
<button (click)="takePictureScaled()" ion-button color="primary">Scaled</button>
<button (click)="takePictureCorrected()" ion-button color="primary">Orientation Corrected</button>
<button (click)="takePictureFile()" ion-button color="primary">File URI</button>
Expand Down
36 changes: 35 additions & 1 deletion example/src/pages/camera/camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import {
Plugins,
CameraDirection,
CameraResultType,
CameraSource,
FilesystemDirectory
Expand Down Expand Up @@ -35,11 +36,44 @@ export class CameraPage {
console.log('ionViewDidLoad CameraPage');
}

async getPhoto() {
async getFileInput() {
const image = await Plugins.Camera.getPhoto({
quality: 90,
allowEditing: true,
resultType: CameraResultType.DataUrl,
webUseInput: true
})
console.log('Got image back', image.path, image.webPath, image.format, image.exif);
this.image = this.sanitizer.bypassSecurityTrustResourceUrl(image && (image.dataUrl));
}

async getPhotoFront() {
const image = await Plugins.Camera.getPhoto({
quality: 90,
allowEditing: true,
direction: CameraDirection.Front,
resultType: CameraResultType.DataUrl
})
console.log('Got image back', image.path, image.webPath, image.format, image.exif);
this.image = this.sanitizer.bypassSecurityTrustResourceUrl(image && (image.dataUrl));
}

async getBase64() {
const image = await Plugins.Camera.getPhoto({
quality: 90,
allowEditing: true,
resultType: CameraResultType.Base64
})
console.log('Got image back', image.base64String, image.format);
this.image = this.sanitizer.bypassSecurityTrustResourceUrl(image && (`data:${image.format};base64,${image.base64String}`));
}

async getPhotoPWAElements() {
const image = await Plugins.Camera.getPhoto({
quality: 90,
allowEditing: true,
resultType: CameraResultType.DataUrl,
source: CameraSource.Camera
})
console.log('Got image back', image.path, image.webPath, image.format, image.exif);
this.image = this.sanitizer.bypassSecurityTrustResourceUrl(image && (image.dataUrl));
Expand Down

0 comments on commit 25505d2

Please sign in to comment.