Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web Camera - use file input #1856

Merged
merged 16 commits into from
Jul 7, 2020
8 changes: 7 additions & 1 deletion core/src/core-plugin-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@ export interface CameraOptions {
* iOS only: The presentation style of the Camera. Defaults to fullscreen.
*/
presentationStyle?: 'fullscreen' | 'popover';

/**
* Web only: whether to use the PWA Element experience or file input. The default
* is to use file input experience. Learn more about PWA Elements: https://capacitor.ionicframework.com/docs/pwa-elements/
mlynch marked this conversation as resolved.
Show resolved Hide resolved
*/
webUsePWAElements?: boolean;
}

export enum CameraSource {
Expand Down Expand Up @@ -347,7 +353,7 @@ export interface CameraPhoto {
*/
exif?: any;
/**
* The format of the image. Currently, only "jpeg" is supported.
* The format of the image, ex: jpeg, png, gif.
imhoffd marked this conversation as resolved.
Show resolved Hide resolved
*/
format: string;
}
Expand Down
110 changes: 86 additions & 24 deletions core/src/web/camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import {
CameraPlugin,
CameraPhoto,
CameraOptions,
CameraResultType
CameraResultType,
CameraDirection
} from '../core-plugin-definitions';

//import '@ionic/pwa-elements';

export class CameraPluginWeb extends WebPlugin implements CameraPlugin {
constructor() {
super({
Expand All @@ -18,36 +17,99 @@ 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);
} else {
resolve(await this._getCameraPhoto(photo, options));
if (options.webUsePWAElements === true) {
const cameraModal: any = document.createElement('pwa-camera-modal');
imhoffd marked this conversation as resolved.
Show resolved Hide resolved
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, reject);
}
} else {
this.fileInputExperience(options, resolve, reject);
}
});
}

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

if (!input) {
input = document.createElement('input') as HTMLInputElement;
input.type = 'file';
input.accept = 'image/*';
mlynch marked this conversation as resolved.
Show resolved Hide resolved

if (options.direction === CameraDirection.Front) {
(input as any).capture = 'user';
} else if (options.direction === CameraDirection.Rear) {
(input as any).capture = 'environment';
}

cameraModal.dismiss();
document.body.removeChild(cameraModal);
input.addEventListener('change', (_e: any) => {
const file = input.files[0];
let format = 'jpeg';
if (file.type === 'image/png') {
format = 'png';
}
if (file.type === 'image/gif') {
format = 'gif';
}

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);
} else {
reject('Unsupported result type for this platform');
}
/*
*/
});

if (options.resultType === CameraResultType.DataUrl || options.resultType === CameraResultType.Base64) {
reader.readAsDataURL(file);
} else {
reject('Camera result type not supported on this platform. Use DataUrl or Base64');
}
});

cameraModal.present();
});
document.body.appendChild(input);
}

input.click();
}

private _getCameraPhoto(photo: Blob, options: CameraOptions) {
return new Promise<CameraPhoto>((resolve, reject) => {
var reader = new FileReader();
var format = photo.type.split('/')[1]
if (options.resultType == CameraResultType.Uri) {
var format = photo.type.split('/')[1];
if (options.resultType === CameraResultType.Uri) {
resolve({
webPath: URL.createObjectURL(photo),
format: format
Expand All @@ -56,7 +118,7 @@ export class CameraPluginWeb extends WebPlugin implements CameraPlugin {
reader.readAsDataURL(photo);
reader.onloadend = () => {
const r = reader.result as string;
if (options.resultType == CameraResultType.DataUrl) {
if (options.resultType === CameraResultType.DataUrl) {
resolve({
dataUrl: r,
format: format
Expand Down
2 changes: 2 additions & 0 deletions example/src/pages/camera/camera.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
<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)="takePicture()" ion-button color="primary">From Camera</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
21 changes: 21 additions & 0 deletions example/src/pages/camera/camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,31 @@ export class CameraPage {
}

async getPhoto() {
const image = await Plugins.Camera.getPhoto({
quality: 90,
allowEditing: true,
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,
webUsePWAElements: true
})
console.log('Got image back', image.path, image.webPath, image.format, image.exif);
this.image = this.sanitizer.bypassSecurityTrustResourceUrl(image && (image.dataUrl));
Expand Down
3 changes: 0 additions & 3 deletions example/www/index.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<script data-ionic="inject">
(function(w){var i=w.Ionic=w.Ionic||{};i.version='^3.9.6';i.angular='5.0.1';i.staticDir='build/';})(window);
</script>
<meta charset="UTF-8">
<title>Ionic App</title>
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
Expand Down