When working on apps with image upload functionality, many times you either need to resize the image or to create something like a preview image for later requests. It’s not a super straight forward thing to to, so here’s how we can create a Thumbnail Image with Ionic.
This Quick Win is about choosing a picture from the library (you can of course also capture a new one) and calculate the size of the image. Then we will create a preview image as a thumbnail from that image and see what size we have with the new image. Spoiler: You can see it inside the little animation below!
Starting our Ionic Thumbnail Image App
We start with a blank Ionic app and install the Camera plugin so we have something to work with. Go ahead and run:
1 2 3 4 |
ionic start previewImage blank cd previewImage ionic cordova plugin add cordova-plugin-camera npm install --save @ionic-native/camera |
Now also make sure to add the Camera to your providers inside the src/app/app.module.ts like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import { BrowserModule } from '@angular/platform-browser'; import { ErrorHandler, NgModule } from '@angular/core'; import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular'; import { SplashScreen } from '@ionic-native/splash-screen'; import { StatusBar } from '@ionic-native/status-bar'; import { MyApp } from './app.component'; import { HomePage } from '../pages/home/home'; import { Camera } from '@ionic-native/camera'; @NgModule({ declarations: [ MyApp, HomePage ], imports: [ BrowserModule, IonicModule.forRoot(MyApp) ], bootstrap: [IonicApp], entryComponents: [ MyApp, HomePage ], providers: [ StatusBar, SplashScreen, {provide: ErrorHandler, useClass: IonicErrorHandler}, Camera ] }) export class AppModule {} |
We don’t need anything else as we will resize the image with the canvas so there’s no need for another external package!
Loading Images and creating a Preview Image
First of all we need our very basic view with buttons to load and image and to create the thumbnail. Additional we have the 2 images plus the size for both of them which we will calculate in the actual class. The view really is very dumb here, so change your src/pages/home/home.html to:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<ion-header> <ion-navbar color="primary"> <ion-title> Image Preview </ion-title> </ion-navbar> </ion-header> <ion-content padding> <button ion-button full (click)="loadImage()">Load Image</button> <button ion-button full (click)="createThumbnail()">Create Thumbnail</button> <h2>Big Image ({{ bigSize }} MB)</h2> <img [src]="bigImg"> <h2>Small Image ({{ smallSize }} MB)</h2> <img [src]="smallImg"> </ion-content> |
Now the actually hard part of this Quick Win begins.
The first step is simple: Inside our loadImage()
function we use the Camera plugin to load an image from the camera. We specify the quality to be 100% so we can see the difference to the thumbnail later.
Of course changing the quality here will also result in a smaller image and most of the time it makes sense to drop at least some percent here. But if you want the best resolution of your users image and also a thumbnail which can be delivered faster, this approach here will work better.
Anyway, after the picture data is there we call our getImageSize()
function to get the size of that image. I didn’t came up with that solution but can’t find the exact solution on SO again. Overall we need to multiplay it with 6 because of how base64 works and divide it by 8 to convert bits into bytes. Of course this means we can also use it like * 3 / 4!
The last additions are to convert that number into MB, so just some more math going on here. But this is not critical to creating our thumbnail.
To create a thumbnail image we call the generateFromImage()
function with a few parameters for the size and quality and get the result inside the callback. This is the function you can copy directly into your code of you need the logic for a thumbnail!
At this point you might also add the functionality for an Ionic Gallery Zoom to show the thumbnail first and then a bigger image later.
The function takes a few steps to create a canvas element with an image. It will draw the image on that canvas with the specific size and afterwards convert the canvas back to a base64 image string but with the specified quality.
In our app we again assign that new image to a variable and calculate the size of it so our view will be updated.
Now go ahead and change your src/pages/home/home.ts to:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import { Camera, CameraOptions } from '@ionic-native/camera'; @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { bigImg = null; bigSize = '0'; smallImg = null; smallSize = '0'; constructor(public navCtrl: NavController, private camera: Camera) { } loadImage() { const options: CameraOptions = { quality: 100, destinationType: this.camera.DestinationType.DATA_URL, sourceType: this.camera.PictureSourceType.PHOTOLIBRARY, correctOrientation: true, allowEdit: false }; this.camera.getPicture(options).then(imageData => { let base64data = 'data:image/jpeg;base64,' + imageData; this.bigImg = base64data; this.bigSize = this.getImageSize(this.bigImg); }, err => { console.log('gallery error: ', err); }); } createThumbnail() { this.generateFromImage(this.bigImg, 200, 200, 0.5, data => { this.smallImg = data; this.smallSize = this.getImageSize(this.smallImg); }); } generateFromImage(img, MAX_WIDTH: number = 700, MAX_HEIGHT: number = 700, quality: number = 1, callback) { var canvas: any = document.createElement("canvas"); var image = new Image(); image.onload = () => { var width = image.width; var height = image.height; if (width > height) { if (width > MAX_WIDTH) { height *= MAX_WIDTH / width; width = MAX_WIDTH; } } else { if (height > MAX_HEIGHT) { width *= MAX_HEIGHT / height; height = MAX_HEIGHT; } } canvas.width = width; canvas.height = height; var ctx = canvas.getContext("2d"); ctx.drawImage(image, 0, 0, width, height); // IMPORTANT: 'jpeg' NOT 'jpg' var dataUrl = canvas.toDataURL('image/jpeg', quality); callback(dataUrl) } image.src = img; } getImageSize(data_url) { var head = 'data:image/jpeg;base64,'; return ((data_url.length - head.length) * 3 / 4 / (1024*1024)).toFixed(4); } } |
That’s all ne weed, and most of it lives in that one function. Make sure to run your app on a device or simulator now as the Cordova plugin won’t work on a browser!
Hopefully this will help you to dramatically reduce the size of your images especially if you work with lists of images where a preview image renders way faster than some 5 MB pictures.
You can also find a video version of this Quick Win below.