I have an input of file type with multiple selection property. When the user selects a file, I compress the files using the JavaScript function. Now I wanted to change the length and width of the images using this function. What should I add to the function for this?
My function is as follows
const compressImage = async (file, { quality = 1, type = file.type }) => {
// Get as image data
const imageBitmap = await createImageBitmap(file);
// Draw to canvas
const canvas = document.createElement('canvas');
canvas.width = imageBitmap.width;
canvas.height = imageBitmap.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(imageBitmap, 0, 0);
// Turn into Blob
const blob = await new Promise((resolve) =>
canvas.toBlob(resolve, type, quality)
);
// Turn Blob into File
return new File([blob], file.name, {
type: blob.type,
});
};
// Get the selected file from the file input
const input = document.querySelector('.my-image-field');
input.addEventListener('change', async (e) => {
// Get the files
const { files } = e.target;
// No files selected
if (!files.length) return;
// We'll store the files in this data transfer object
const dataTransfer = new DataTransfer();
// For every file in the files list
for (const file of files) {
// We don't have to compress files that aren't images
if (!file.type.startsWith('image')) {
// Ignore this file, but do add it to our result
dataTransfer.items.add(file);
continue;
}
// We compress the file by 50%
const compressedFile = await compressImage(file, {
quality: 0.3,
type: 'image/jpeg',
});
// Save back the compressed file instead of the original file
dataTransfer.items.add(compressedFile);
}
// Set value of the file input to our new files list
e.target.files = dataTransfer.files;
});
There are at least two options:
scale()
method:The CanvasRenderingContext2D.scale() method of the Canvas 2D API adds a scaling transformation to the canvas units horizontally and/or vertically.
drawImage()
:drawImage(image, dx, dy, dWidth, dHeight)
The width to draw the image in the destination canvas. This allows scaling of the drawn image.
The height to draw the image in the destination canvas. This allows scaling of the drawn image.
In the following example a scale()
method is used with the same scaling factor for width and height. A modification of the image input's files
is disabled to avoid repetitive scaling, but it can be reenabled by uncommenting e.target.files = dataTransfer.files;
.
const compressImage = async(file, {
quality = 1,
type = file.type,
scalingFactor = 1
}) => {
// Get as image data
const imageBitmap = await createImageBitmap(file);
// Draw to canvas
const canvas = document.createElement('canvas');
canvas.width = imageBitmap.width * scalingFactor;
canvas.height = imageBitmap.height * scalingFactor;
const ctx = canvas.getContext('2d');
ctx.scale(scalingFactor, scalingFactor);
ctx.drawImage(imageBitmap, 0, 0);
// Turn into Blob
const blob = await new Promise((resolve) =>
canvas.toBlob(resolve, type, quality)
);
// Turn Blob into File
return new File([blob], file.name, {
type: blob.type,
});
};
// Get the selected file from the file input
const input = document.querySelector('.my-image-field');
input.addEventListener('change', async(e) => {
// Get the files
const {
files
} = e.target;
// No files selected
if (!files.length) return;
// We'll store the files in this data transfer object
const dataTransfer = new DataTransfer();
// Clear the image previews container
previewsContainer.innerHTML = '';
// For every file in the files list
for (const file of files) {
// We don't have to compress files that aren't images
if (!file.type.startsWith('image')) {
// Ignore this file, but do add it to our result
dataTransfer.items.add(file);
continue;
}
// We compress the file by 50%
const compressedFile = await compressImage(file, {
quality: parseFloat(qualityInput.value),
type: 'image/jpeg',
scalingFactor: parseFloat(scalingInput.value)
});
// Save back the compressed file instead of the original file
dataTransfer.items.add(compressedFile);
// Show scaled images
const img = document.createElement('img');
img.src = URL.createObjectURL(compressedFile);
previewsContainer.appendChild(img);
}
// UNCOMMENT TO:
// Set value of the file input to our new files list
// e.target.files = dataTransfer.files;
});
const previewsContainer = document.querySelector('.image-previews');
const scalingInput = document.querySelector('.scaling-factor');
const qualityInput = document.querySelector('.quality');
const updateRangeOutput = (inp) => {
inp.nextElementSibling.innerText = inp.value;
};
[scalingInput, qualityInput].forEach(inp => {
inp.addEventListener('change', () => {
updateRangeOutput(inp);
input.dispatchEvent(new Event('change'));
});
updateRangeOutput(inp);
});
<p>
Choose images:
<input type="file" accept="image/*" multiple class="my-image-field">
</p>
<p>
Scaling factor:
<input type="range" class="scaling-factor" value="0.5" min="0.1" max="2" step="0.1">
<span>0.5</span>
</p>
<p>
Quality:
<input type="range" class="quality" value="0.5" min="0.1" max="1" step="0.1">
<span>0.5</span>
</p>
<p class="image-previews"></p>