Browser support for a JavaScript function? - javascript

I have a function which takes an image as a input and returns it as a 200 x 200 size.
It's working on local platform on Chrome. But not working on Safari by codepen site.
https://codepen.io/sbrakeshrath/pen/PoprEqP?editors=1011
I don't know why.
Honestly my friend said that but I can't test as I have no Mac devices.
Can you look at my code and say what's wrong with it and the browser support for entire code?
Another Favor
Can you say am I doing it on the right way or any other possibility is there?
const Pro_pic = document.querySelector("img");
// console.log(Pro_pic);
function changeProfilePic(e, Pro_pic, form) {
console.log("hallo");
const pic_dim = {
w: 250,
h: 250,
};
const imgIn = document.createElement("input");
imgIn.type = "file";
imgIn.accept = "image/*";
imgIn.addEventListener("change", (e) => {
const file = e.target.files[0];
const objUrl = URL.createObjectURL(file);
var img = new Image();
img.src = objUrl;
img.onload = (e) => {
const ratio = {
h: img.height / img.width,
v: img.width / img.height,
};
// canvas element
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = pic_dim.w;
canvas.height = pic_dim.h;
const newImfDim = {
w: 0,
h: 0,
};
if (ratio.v < 1) {
newImfDim.w = pic_dim.w;
newImfDim.h = pic_dim.h / ratio.v;
} else {
newImfDim.w = pic_dim.w / ratio.h;
newImfDim.h = pic_dim.h;
}
console.log(newImfDim);
canvas.style.margin = "20px 20px";
ctx.drawImage(
img,
-(newImfDim.w - pic_dim.w) / 2,
-(newImfDim.h - pic_dim.h) / 2,
newImfDim.w,
newImfDim.h
);
Pro_pic.src = canvas.toDataURL();
form.insertBefore(canvas, form.firstChild);
};
});
imgIn.click();
}
function clicked(e){
changeProfilePic(e, Pro_pic, document.body);
}
<img src= "" />
<button onclick = "clicked(event)"> click to change pic</button>

Related

iOS resizing image with canvas results in black image

I am trying to resize multiple images in the browser (same results in Chrome and Safari) by using canvas, to then save them to IndexedDB via Dexie.js. It all works fine in Android, but on iOS (tested on iPhone 8 and X) 1 or 2 random pictures result in black images.
What might be the issue? This is the function I am using:
photoHeightMax = 1100;
photoWidthMax = 1100;
resizeBase64Img(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = src;
img.onload = () => {
const imgWidth = img.width;
const imgHeight = img.height;
const scale = Math.min(this.photoWidthMax / imgWidth, this.photoHeightMax / imgHeight);
const widthScaled = scale > 1 ? imgWidth : imgWidth * scale;
const heightScaled = scale > 1 ? imgHeight : imgHeight * scale;
const elem = document.createElement('canvas');
elem.width = widthScaled;
elem.height = heightScaled;
const ctx = elem.getContext('2d');
ctx.drawImage(img, 0, 0, widthScaled, heightScaled);
const data = ctx.canvas.toDataURL('image/jpeg', 0.85);
resolve(data);
};
img.onerror = (error) => reject(error);
});
}
Any help would be appreciated!

How to add class to element on canvas?

I would like to add a class in my FirstImg in my code so I can modify it in my CSS but it's not working :
onImageFetch = () =>
{
const imageFirst = 'myfirstimg.jpg';
const imgSecond = 'mysecondimg.jpg';
const canvas = this.canvas;
const img = new Image();
const secondImg = new Image();
img.height = 400;
img.width = 400;
img.padding = 10;
img.classList.add('image-canvas'); // Here I want to add my class
secondImg.padding = 10;
img.onload = () =>
{
let ctx = canvas.getContext('2d');
const width = img.width;
const height = img.height;
ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
ctx.drawImage(secondImg, 260, 30, 70, 25);
};
img.src = imageFirst;
secondImg.src = imgSecond;
}
and my CSS will be something like this :
.myNewClassCreate {
height: 50px;
width: 50px;
}
I tried img.className = 'myclass'; and img.class = 'myclass'; but it's not working.
Need some help, thanks you !

Recording video stream with img filter overlay

I am using a face tracking library (tracking.js) to capture a video stream and place an image on top of the face.
The image is drawn on a canvas, which has the same width and height as the video therefore, the overlay.
I am trying to take a picture and video of the stream + canvas image, however O can only get a crude implementation of the stream and image that is distorted.
Here is a CodePen
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const tracker = new tracking.ObjectTracker('face');
const flowerCrownButton = document.getElementById('flower-crown');
tracker.setInitialScale(1);
tracker.setStepSize(2.7);
tracker.setEdgesDensity(.2);
const img = document.createElement("img");
img.setAttribute("id", "pic");
img.src = canvas.toDataURL();
let filterX = 0;
let filterY = 0;
let filterWidth = 0;
let filterHeight = 0;
function changePic(x, y, width, height, src) {
img.src = src;
filterX = x;
filterY = y;
filterWidth = width;
filterHeight = height;
}
function flowerCrown() {
changePic(0, -0.5, 1, 1, 'https://s3-us-west-
2. amazonaws.com / s.cdpn.io / 450347 / flower - crown.png ')
}
flowerCrownButton.addEventListener('click', flowerCrown);
//listen for track events
tracker.on('track', function(event) {
//if (event.data.length === 0) {
//alert("No objects were detected in this frame.");
//} else {
context.clearRect(0, 0, canvas.width, canvas.height)
event.data.forEach(rect => {
context.drawImage(img, rect.x + (filterX * rect.width),
rect.y + (filterY * rect.height),
rect.width * filterWidth,
rect.height * filterHeight
)
})
//}// end of else
});
//start tracking
tracking.track('#video', tracker, {
camera: true
})
const canvas2 = document.getElementById('canvas2');
const context2 = canvas2.getContext('2d');
const video = document.getElementById("video");
video.addEventListener("loadedmetadata", function() {
ratio = video.videoWidth / video.videoHeight;
w = video.videoWidth - 100;
h = parseInt(w / ratio, 10);
canvas2.width = w;
canvas2.height = h;
}, false);
function snap() {
context2.drawImage(video, 10, 5);
context2.drawImage(img, 10, 10)
}
}
Any ideas? I prefer to use the media recorder API and have tried it, but again could not get a stream or picture with the image filter overlay.
Thanks and please don't be snarky :)

Resize images on front-end [duplicate]

I have found a few different posts and even questions on stackoverflow answering this question. I am basically implementing this same thing as this post.
So here is my issue. When I upload the photo, I also need to submit the rest of the form. Here is my html:
<form id="uploadImageForm" enctype="multipart/form-data">
<input name="imagefile[]" type="file" id="takePictureField" accept="image/*" onchange="uploadPhotos(\'#{imageUploadUrl}\')" />
<input id="name" value="#{name}" />
... a few more inputs ...
</form>
Previously, I did not need to resize the image, so my javascript looked like this:
window.uploadPhotos = function(url){
var data = new FormData($("form[id*='uploadImageForm']")[0]);
$.ajax({
url: url,
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
... handle error...
}
}
});
};
This all worked great... now that I need to resize the images... how can I replace the image in the form so that the resized one is posted and not the uploaded image?
window.uploadPhotos = function(url){
var resizedImage;
// Read in file
var file = event.target.files[0];
// Ensure it's an image
if(file.type.match(/image.*/)) {
console.log('An image has been loaded');
// Load the image
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {
// Resize the image
var canvas = document.createElement('canvas'),
max_size = 1200,
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
resizedImage = canvas.toDataURL('image/jpeg');
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(file);
}
// TODO: Need some logic here to switch out which photo is being posted...
var data = new FormData($("form[id*='uploadImageForm']")[0]);
$.ajax({
url: url,
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
... handle error...
}
}
});
};
I've thought about moving the file input out of the form and having a hidden input in the form that I set the value of to the value of the resized image... But I'm wondering if I can just replace the image that is already in the form.
Here is what I ended up doing and it worked great.
First I moved the file input outside of the form so that it is not submitted:
<input name="imagefile[]" type="file" id="takePictureField" accept="image/*" onchange="uploadPhotos(\'#{imageUploadUrl}\')" />
<form id="uploadImageForm" enctype="multipart/form-data">
<input id="name" value="#{name}" />
... a few more inputs ...
</form>
Then I changed the uploadPhotos function to handle only the resizing:
window.uploadPhotos = function(url){
// Read in file
var file = event.target.files[0];
// Ensure it's an image
if(file.type.match(/image.*/)) {
console.log('An image has been loaded');
// Load the image
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {
// Resize the image
var canvas = document.createElement('canvas'),
max_size = 544,// TODO : pull max size from a site config
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
var dataUrl = canvas.toDataURL('image/jpeg');
var resizedImage = dataURLToBlob(dataUrl);
$.event.trigger({
type: "imageResized",
blob: resizedImage,
url: dataUrl
});
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(file);
}
};
As you can see I'm using canvas.toDataURL('image/jpeg'); to change the resized image into a dataUrl adn then I call the function dataURLToBlob(dataUrl); to turn the dataUrl into a blob that I can then append to the form. When the blob is created, I trigger a custom event. Here is the function to create the blob:
/* Utility function to convert a canvas to a BLOB */
var dataURLToBlob = function(dataURL) {
var BASE64_MARKER = ';base64,';
if (dataURL.indexOf(BASE64_MARKER) == -1) {
var parts = dataURL.split(',');
var contentType = parts[0].split(':')[1];
var raw = parts[1];
return new Blob([raw], {type: contentType});
}
var parts = dataURL.split(BASE64_MARKER);
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {type: contentType});
}
/* End Utility function to convert a canvas to a BLOB */
Finally, here is my event handler that takes the blob from the custom event, appends the form and then submits it.
/* Handle image resized events */
$(document).on("imageResized", function (event) {
var data = new FormData($("form[id*='uploadImageForm']")[0]);
if (event.blob && event.url) {
data.append('image_data', event.blob);
$.ajax({
url: event.url,
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
//handle errors...
}
});
}
});
if any interested I've made a typescript version:
interface IResizeImageOptions {
maxSize: number;
file: File;
}
const resizeImage = (settings: IResizeImageOptions) => {
const file = settings.file;
const maxSize = settings.maxSize;
const reader = new FileReader();
const image = new Image();
const canvas = document.createElement('canvas');
const dataURItoBlob = (dataURI: string) => {
const bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
atob(dataURI.split(',')[1]) :
unescape(dataURI.split(',')[1]);
const mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
const max = bytes.length;
const ia = new Uint8Array(max);
for (var i = 0; i < max; i++) ia[i] = bytes.charCodeAt(i);
return new Blob([ia], {type:mime});
};
const resize = () => {
let width = image.width;
let height = image.height;
if (width > height) {
if (width > maxSize) {
height *= maxSize / width;
width = maxSize;
}
} else {
if (height > maxSize) {
width *= maxSize / height;
height = maxSize;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
let dataUrl = canvas.toDataURL('image/jpeg');
return dataURItoBlob(dataUrl);
};
return new Promise((ok, no) => {
if (!file.type.match(/image.*/)) {
no(new Error("Not an image"));
return;
}
reader.onload = (readerEvent: any) => {
image.onload = () => ok(resize());
image.src = readerEvent.target.result;
};
reader.readAsDataURL(file);
})
};
and here's the javascript result:
var resizeImage = function (settings) {
var file = settings.file;
var maxSize = settings.maxSize;
var reader = new FileReader();
var image = new Image();
var canvas = document.createElement('canvas');
var dataURItoBlob = function (dataURI) {
var bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
atob(dataURI.split(',')[1]) :
unescape(dataURI.split(',')[1]);
var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
var max = bytes.length;
var ia = new Uint8Array(max);
for (var i = 0; i < max; i++)
ia[i] = bytes.charCodeAt(i);
return new Blob([ia], { type: mime });
};
var resize = function () {
var width = image.width;
var height = image.height;
if (width > height) {
if (width > maxSize) {
height *= maxSize / width;
width = maxSize;
}
} else {
if (height > maxSize) {
width *= maxSize / height;
height = maxSize;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
var dataUrl = canvas.toDataURL('image/jpeg');
return dataURItoBlob(dataUrl);
};
return new Promise(function (ok, no) {
if (!file.type.match(/image.*/)) {
no(new Error("Not an image"));
return;
}
reader.onload = function (readerEvent) {
image.onload = function () { return ok(resize()); };
image.src = readerEvent.target.result;
};
reader.readAsDataURL(file);
});
};
usage is like:
resizeImage({
file: $image.files[0],
maxSize: 500
}).then(function (resizedImage) {
console.log("upload resized image")
}).catch(function (err) {
console.error(err);
});
or (async/await):
const config = {
file: $image.files[0],
maxSize: 500
};
const resizedImage = await resizeImage(config)
console.log("upload resized image")
If some of you, like me, encounter orientation problems I have combined the solutions here with a exif orientation fix
https://gist.github.com/SagiMedina/f00a57de4e211456225d3114fd10b0d0
I have made my own version without actually using the file reader. Instead I use createObjectUrl which is supported by majority of modern browsers.
/**
* Function scaling an image from a file input to specified dimensions
* If the specified dimensions are not proportional to image dimensions the output image will be cropped at center
*
* #param file {File} Input file of a form
* #param dimensions {{width: number, height: number}} Dimenstions of the output image
* #returns {Promise<Blob | null>} Promise resolving to a scale image or a null if provided an invalid file type
*/
export async function scaleImageBeforeUpload(file: File, dimensions: {width: number, height: number}): Promise<Blob | null> {
// ensure the file is an image
if (!file.type.match(/image.*/)) return null;
const image = new Image();
image.src = URL.createObjectURL(file);
await new Promise<Event>((res) => image.onload = res);
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d", {alpha: true});
canvas.width = dimensions.width;
canvas.height = dimensions.height;
if (image.height <= image.width) {
const scaleProportions = canvas.height / image.height;
const scaledWidth = scaleProportions * image.width;
context.drawImage(image, (canvas.width - scaledWidth)/2, 0, scaledWidth, canvas.height);
}
else {
const scaleProportions = canvas.width / image.width;
const scaledHeight = scaleProportions * image.height;
context.drawImage(image, 0, (canvas.height - scaledHeight)/2, canvas.width, scaledHeight);
}
return new Promise((res) => canvas.toBlob(res));
}
In 2022 we have some new APIs available to us. This is the solution I came up with. We don't have to mess with FileReader API or image onload callback.
The following code accepts a file object or a Blob object and outputs a blob of a cropped, centered, resized image, and also converts it to webp.
export default async (file, size) => {
size ??= 256
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = size
canvas.height = size
const bitmap = await createImageBitmap(file)
const { width, height } = bitmap
const ratio = Math.max(size / width, size / height)
const x = (size - (width * ratio)) / 2
const y = (size - (height * ratio)) / 2
ctx.drawImage(bitmap, 0, 0, width, height, x, y, width * ratio, height * ratio)
return new Promise(resolve => {
canvas.toBlob(blob => {
resolve(blob)
}, 'image/webp', 1)
})
}

Use HTML5 to resize an image before upload

I have found a few different posts and even questions on stackoverflow answering this question. I am basically implementing this same thing as this post.
So here is my issue. When I upload the photo, I also need to submit the rest of the form. Here is my html:
<form id="uploadImageForm" enctype="multipart/form-data">
<input name="imagefile[]" type="file" id="takePictureField" accept="image/*" onchange="uploadPhotos(\'#{imageUploadUrl}\')" />
<input id="name" value="#{name}" />
... a few more inputs ...
</form>
Previously, I did not need to resize the image, so my javascript looked like this:
window.uploadPhotos = function(url){
var data = new FormData($("form[id*='uploadImageForm']")[0]);
$.ajax({
url: url,
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
... handle error...
}
}
});
};
This all worked great... now that I need to resize the images... how can I replace the image in the form so that the resized one is posted and not the uploaded image?
window.uploadPhotos = function(url){
var resizedImage;
// Read in file
var file = event.target.files[0];
// Ensure it's an image
if(file.type.match(/image.*/)) {
console.log('An image has been loaded');
// Load the image
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {
// Resize the image
var canvas = document.createElement('canvas'),
max_size = 1200,
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
resizedImage = canvas.toDataURL('image/jpeg');
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(file);
}
// TODO: Need some logic here to switch out which photo is being posted...
var data = new FormData($("form[id*='uploadImageForm']")[0]);
$.ajax({
url: url,
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
... handle error...
}
}
});
};
I've thought about moving the file input out of the form and having a hidden input in the form that I set the value of to the value of the resized image... But I'm wondering if I can just replace the image that is already in the form.
Here is what I ended up doing and it worked great.
First I moved the file input outside of the form so that it is not submitted:
<input name="imagefile[]" type="file" id="takePictureField" accept="image/*" onchange="uploadPhotos(\'#{imageUploadUrl}\')" />
<form id="uploadImageForm" enctype="multipart/form-data">
<input id="name" value="#{name}" />
... a few more inputs ...
</form>
Then I changed the uploadPhotos function to handle only the resizing:
window.uploadPhotos = function(url){
// Read in file
var file = event.target.files[0];
// Ensure it's an image
if(file.type.match(/image.*/)) {
console.log('An image has been loaded');
// Load the image
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {
// Resize the image
var canvas = document.createElement('canvas'),
max_size = 544,// TODO : pull max size from a site config
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
var dataUrl = canvas.toDataURL('image/jpeg');
var resizedImage = dataURLToBlob(dataUrl);
$.event.trigger({
type: "imageResized",
blob: resizedImage,
url: dataUrl
});
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(file);
}
};
As you can see I'm using canvas.toDataURL('image/jpeg'); to change the resized image into a dataUrl adn then I call the function dataURLToBlob(dataUrl); to turn the dataUrl into a blob that I can then append to the form. When the blob is created, I trigger a custom event. Here is the function to create the blob:
/* Utility function to convert a canvas to a BLOB */
var dataURLToBlob = function(dataURL) {
var BASE64_MARKER = ';base64,';
if (dataURL.indexOf(BASE64_MARKER) == -1) {
var parts = dataURL.split(',');
var contentType = parts[0].split(':')[1];
var raw = parts[1];
return new Blob([raw], {type: contentType});
}
var parts = dataURL.split(BASE64_MARKER);
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {type: contentType});
}
/* End Utility function to convert a canvas to a BLOB */
Finally, here is my event handler that takes the blob from the custom event, appends the form and then submits it.
/* Handle image resized events */
$(document).on("imageResized", function (event) {
var data = new FormData($("form[id*='uploadImageForm']")[0]);
if (event.blob && event.url) {
data.append('image_data', event.blob);
$.ajax({
url: event.url,
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
//handle errors...
}
});
}
});
if any interested I've made a typescript version:
interface IResizeImageOptions {
maxSize: number;
file: File;
}
const resizeImage = (settings: IResizeImageOptions) => {
const file = settings.file;
const maxSize = settings.maxSize;
const reader = new FileReader();
const image = new Image();
const canvas = document.createElement('canvas');
const dataURItoBlob = (dataURI: string) => {
const bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
atob(dataURI.split(',')[1]) :
unescape(dataURI.split(',')[1]);
const mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
const max = bytes.length;
const ia = new Uint8Array(max);
for (var i = 0; i < max; i++) ia[i] = bytes.charCodeAt(i);
return new Blob([ia], {type:mime});
};
const resize = () => {
let width = image.width;
let height = image.height;
if (width > height) {
if (width > maxSize) {
height *= maxSize / width;
width = maxSize;
}
} else {
if (height > maxSize) {
width *= maxSize / height;
height = maxSize;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
let dataUrl = canvas.toDataURL('image/jpeg');
return dataURItoBlob(dataUrl);
};
return new Promise((ok, no) => {
if (!file.type.match(/image.*/)) {
no(new Error("Not an image"));
return;
}
reader.onload = (readerEvent: any) => {
image.onload = () => ok(resize());
image.src = readerEvent.target.result;
};
reader.readAsDataURL(file);
})
};
and here's the javascript result:
var resizeImage = function (settings) {
var file = settings.file;
var maxSize = settings.maxSize;
var reader = new FileReader();
var image = new Image();
var canvas = document.createElement('canvas');
var dataURItoBlob = function (dataURI) {
var bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
atob(dataURI.split(',')[1]) :
unescape(dataURI.split(',')[1]);
var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
var max = bytes.length;
var ia = new Uint8Array(max);
for (var i = 0; i < max; i++)
ia[i] = bytes.charCodeAt(i);
return new Blob([ia], { type: mime });
};
var resize = function () {
var width = image.width;
var height = image.height;
if (width > height) {
if (width > maxSize) {
height *= maxSize / width;
width = maxSize;
}
} else {
if (height > maxSize) {
width *= maxSize / height;
height = maxSize;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
var dataUrl = canvas.toDataURL('image/jpeg');
return dataURItoBlob(dataUrl);
};
return new Promise(function (ok, no) {
if (!file.type.match(/image.*/)) {
no(new Error("Not an image"));
return;
}
reader.onload = function (readerEvent) {
image.onload = function () { return ok(resize()); };
image.src = readerEvent.target.result;
};
reader.readAsDataURL(file);
});
};
usage is like:
resizeImage({
file: $image.files[0],
maxSize: 500
}).then(function (resizedImage) {
console.log("upload resized image")
}).catch(function (err) {
console.error(err);
});
or (async/await):
const config = {
file: $image.files[0],
maxSize: 500
};
const resizedImage = await resizeImage(config)
console.log("upload resized image")
If some of you, like me, encounter orientation problems I have combined the solutions here with a exif orientation fix
https://gist.github.com/SagiMedina/f00a57de4e211456225d3114fd10b0d0
I have made my own version without actually using the file reader. Instead I use createObjectUrl which is supported by majority of modern browsers.
/**
* Function scaling an image from a file input to specified dimensions
* If the specified dimensions are not proportional to image dimensions the output image will be cropped at center
*
* #param file {File} Input file of a form
* #param dimensions {{width: number, height: number}} Dimenstions of the output image
* #returns {Promise<Blob | null>} Promise resolving to a scale image or a null if provided an invalid file type
*/
export async function scaleImageBeforeUpload(file: File, dimensions: {width: number, height: number}): Promise<Blob | null> {
// ensure the file is an image
if (!file.type.match(/image.*/)) return null;
const image = new Image();
image.src = URL.createObjectURL(file);
await new Promise<Event>((res) => image.onload = res);
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d", {alpha: true});
canvas.width = dimensions.width;
canvas.height = dimensions.height;
if (image.height <= image.width) {
const scaleProportions = canvas.height / image.height;
const scaledWidth = scaleProportions * image.width;
context.drawImage(image, (canvas.width - scaledWidth)/2, 0, scaledWidth, canvas.height);
}
else {
const scaleProportions = canvas.width / image.width;
const scaledHeight = scaleProportions * image.height;
context.drawImage(image, 0, (canvas.height - scaledHeight)/2, canvas.width, scaledHeight);
}
return new Promise((res) => canvas.toBlob(res));
}
In 2022 we have some new APIs available to us. This is the solution I came up with. We don't have to mess with FileReader API or image onload callback.
The following code accepts a file object or a Blob object and outputs a blob of a cropped, centered, resized image, and also converts it to webp.
export default async (file, size) => {
size ??= 256
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = size
canvas.height = size
const bitmap = await createImageBitmap(file)
const { width, height } = bitmap
const ratio = Math.max(size / width, size / height)
const x = (size - (width * ratio)) / 2
const y = (size - (height * ratio)) / 2
ctx.drawImage(bitmap, 0, 0, width, height, x, y, width * ratio, height * ratio)
return new Promise(resolve => {
canvas.toBlob(blob => {
resolve(blob)
}, 'image/webp', 1)
})
}

Categories

Resources