preface
Uploading images/videos/files is a common problem, but once the image is too large, it can lead to a bad operation experience. Image uploading is a common business scenario in the front-end. Whether foreground or background, appropriate image compression processing, can significantly improve the user experience. And in the background management system, picture compression can not only improve the background administrator operation experience, but also can prevent the background setting too large pictures lead to the foreground picture loading too long, thus affecting the user experience.
About Compression
thinking
Think about the basic process of compressing images
- Input is read into a file and converted to Base64 encoding using FileReader
- Create img with its SRC pointing to base64
- Create a new canvas and draw IMG on the canvas
- Export canvas as Base64 or Blob using Canvas. ToDataURL /toBlob
- Convert base64 or Blob to File
Breaking down these steps one by one, we’ll see that it seems to involve image quality in the Canvas. ToDataURL, so let’s start there.
To prepare
HTMLCanvasElement.toDataURL()
HTMLCanvasElement. ToDataURL () method returns a data URI contains pictures show. You can use the type parameter, which defaults to PNG. The resolution of the image is 96dpi.
- If the height or width of the canvas is 0, the string “data:,” is returned.
- If the passed type is not “image/ PNG”, but the returned value begins with “data:image/ PNG”, then the passed type is not supported.
grammar
canvas.toDataURL(type, encoderOptions);
parameter
- The type of the optional
Image format, default is image/ PNG
- EncoderOptions optional
When the image format is specified as Image/JPEG or Image /webp, the image quality can be selected from a range of 0 to 1. If the value is out of range, the default value 0.92 will be used. Other parameters are ignored. Chrome supports the “image/webp” type.
guess
Maybe the smaller the second parameter (quality) of toDataURL(type,quality) is, the smaller the file size is
practice
If you look at the original image, the size is 7.31 KB
ToDataURL (type,quality) Quality default: 0.92
<input id="fileInput" type="file" />
<img id="img" src="" alt="">
Copy the code
let fileId = document.getElementById('fileInput')
let img = document.getElementById('img')
fileId.onchange = function (e) {
let file = e.target.files[0]
compressImg(file, 0.92).then(res= > {// See the appendix for the compressImg method
console.log(res)
img.src = window.URL.createObjectURL(res.file); })}Copy the code
For the compressImg method, see the appendix
You can see that the image size is8.83 KB
After compression, the picture is bigger instead. How is this?
It seems that the original guess was not quite right, so let’s keep looking.
fileId.onchange = function (e) {
let file = e.target.files[0]
compressImg(file, 0.1).then(res= > {// See the appendix for the compressImg method
console.log(res)
img.src = window.URL.createObjectURL(res.file); })}Copy the code
当quality
For 0.1, the image is only1.63 KB
, the same quality also decreased
Continue to… A long time later
So let’s do it on a line chart to make it more intuitive
I took a few more images and asked them to use the default value of 0.92, all of which were larger than the original
Therefore, the default value of the image is often larger than the original
Let’s take a look at what quality can maximize the compression efficiency of the image
Maximize compression efficiency, that is, maximize compression without affecting image quality
After trying a series of images, I found that when the quality is between 0.2 and 0.5, the image quality does not change much. The smaller the quality value is, the more impressive the compression efficiency is (that is, when the quality is around 0.2, the compression image can be maximized without much impact on the image quality).
conclusion
After practice, it can be concluded that the default value of the image is often higher than the original image quality.
whenquality
Between 0.2 and 0.5, the image quality does not change much,quality
The smaller the value of, the more impressive the compression efficiency (that is, at about 0.2, the compression image can be maximized, while the image quality is not too much affected)
The appendix
/** * Compression method *@param {string} The file file *@param {Number} Quality The value ranges from 0 to 1 */
function compressImg(file, quality) {
if (file[0]) {
return Promise.all(Array.from(file).map(e= > compressImg(e,
quality))) // Return a Promise array if it is a file array
} else {
return new Promise((resolve) = > {
const reader = new FileReader() / / create FileReader
reader.onload = ({ target: { result: src } }) = > {
const image = new Image() // Create the img element
image.onload = async() = > {const canvas = document.createElement('canvas') // Create canvas element
canvas.width = image.width
canvas.height = image.height
canvas.getContext('2d').drawImage(image, 0.0, image.width, image.height) / / drawing canvas
const canvasURL = canvas.toDataURL('image/jpeg', quality)
const buffer = atob(canvasURL.split(', ') [1])
let length = buffer.length
const bufferArray = new Uint8Array(new ArrayBuffer(length))
while (length--) {
bufferArray[length] = buffer.charCodeAt(length)
}
const miniFile = new File([bufferArray], file.name, {
type: 'image/jpeg'
})
resolve({
file: miniFile,
origin: file,
beforeSrc: src,
afterSrc: canvasURL,
beforeKB: Number((file.size / 1024).toFixed(2)),
afterKB: Number((miniFile.size / 1024).toFixed(2))
})
}
image.src = src
}
reader.readAsDataURL(file)
})
}
}
Copy the code
upgrading
Boundary problems were found in the process of use, that is, the image size is too large, IOS size limit, PNG transparent image black and other problems
Therefore, the large size image is optimized and scaled down, which greatly improves the compression efficiency
For example, take a 14M, 6016X4016 image
a14M
The original image (6016X4016) is left after compression (1400X935) on the premise of changing only the size without changing the mass139.62 KB
It is conceivable that the limitation of size can maximize the compression efficiency
/** * Compression method *@param {file} The file file *@param {Number} Quality Image quality (between 0 and 1, the value is 0.92 by default) */
compressImg(file, quality) {
var qualitys = 0.52
console.log(parseInt((file.size / 1024).toFixed(2)))
if (parseInt((file.size / 1024).toFixed(2))"1024) {
qualitys = 0.85
}
if (5 * 1024 < parseInt((file.size / 1024).toFixed(2))) {
qualitys = 0.92
}
if (quality) {
qualitys = quality
}
if (file[0]) {
return Promise.all(Array.from(file).map(e= > this.compressImg(e,
qualitys))) // Return a Promise array if it is a file array
} else {
return new Promise((resolve) = > {
console.log(file)
if ((file.size / 1024).toFixed(2) < 300) {
resolve({
file: file
})
} else {
const reader = new FileReader() / / create FileReader
reader.onload = ({ target: { result: src } }) = > {
const image = new Image() // Create the img element
image.onload = async() = > {const canvas = document.createElement('canvas') // Create canvas element
const context = canvas.getContext('2d')
var targetWidth = image.width
var targetHeight = image.height
var originWidth = image.width
var originHeight = image.height
if (1 * 1024< =parseInt((file.size / 1024).toFixed(2&&))parseInt((file.size / 1024).toFixed(2)) < =10 * 1024) {
var maxWidth = 1600
var maxHeight = 1600
targetWidth = originWidth
targetHeight = originHeight
// The image size exceeds the limit
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// Wider. The size is defined according to the width
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
if (10 * 1024< =parseInt((file.size / 1024).toFixed(2&&))parseInt((file.size / 1024).toFixed(2)) < =20 * 1024) {
maxWidth = 1400
maxHeight = 1400
targetWidth = originWidth
targetHeight = originHeight
// The image size exceeds the limit
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// Wider. The size is defined according to the width
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0.0, targetWidth, targetHeight)
context.drawImage(image, 0.0, targetWidth, targetHeight) / / drawing canvas
const canvasURL = canvas.toDataURL('image/jpeg', qualitys)
const buffer = atob(canvasURL.split(', ') [1])
let length = buffer.length
const bufferArray = new Uint8Array(new ArrayBuffer(length))
while (length--) {
bufferArray[length] = buffer.charCodeAt(length)
}
const miniFile = new File([bufferArray], file.name, {
type: 'image/jpeg'
})
console.log({
file: miniFile,
origin: file,
beforeSrc: src,
afterSrc: canvasURL,
beforeKB: Number((file.size / 1024).toFixed(2)),
afterKB: Number((miniFile.size / 1024).toFixed(2)),
qualitys: qualitys
})
resolve({
file: miniFile,
origin: file,
beforeSrc: src,
afterSrc: canvasURL,
beforeKB: Number((file.size / 1024).toFixed(2)),
afterKB: Number((miniFile.size / 1024).toFixed(2))
})
}
image.src = src
}
reader.readAsDataURL(file)
}
})
}
},
Copy the code
Write in the last
I am Liangcheng A, a front end, who loves technology and life.
I’m glad to meet you.
If you want to learn more, please click here and look forward to your little ⭐⭐
-
Feel free to correct any mistakes in the comments section, and if this post helped you, feel free to like and follow 😊
-
This article was originally published in Nuggets and cannot be reproduced without permission at 💌