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
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.
imhoffd marked this conversation as resolved.
Show resolved Hide resolved
*
* 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