предисловие
Загрузка изображений/видео/файлов — это проблема, с которой мы часто сталкиваемся, но как только изображение станет слишком большим, это приведет к ухудшению работы. Загрузка изображений — распространенный бизнес-сценарий во внешнем интерфейсе. Будь то передний план или фон, изображение правильно сжато, Это может значительно улучшить пользовательский опыт. В системе управления фоном сжатие изображения может не только улучшить работу администратора фонового изображения, но и предотвратить установку слишком большого фонового изображения, что приведет к слишком долгой загрузке основного изображения, что повлияет на работу пользователя.
О сжатии изображений
думать
Подумайте об основном процессе сжатия изображений.
- ввод для чтения файла, используйте FileReader для преобразования его в кодировку base64
- Создайте новый img, чтобы его src прямо сейчас указывал на base64
- Создайте новый холст и нарисуйте img на холсте.
- Экспортировать холст как base64 или Blob с помощью canvas.toDataURL/toBlob
- Преобразование base64 или BLOB-объекта в файл
Разбирая эти шаги один за другим, мы обнаружим, что качество изображения, похоже, связано с canvas.toDataURL, так что давайте начнем отсюда.
Подготовить
HTMLCanvasElement.toDataURL()
Метод HTMLCanvasElement.toDataURL() возвращает URI данных, содержащий отображаемое изображение. Тип можно использовать с параметром type, который по умолчанию имеет формат PNG. Разрешение картинки 96dpi.
- Если высота или ширина холста равна 0, возвращается строка «данные:».
- Если входящий тип не "image/png", но возвращаемое значение начинается с "data:image/png", то входящий тип не поддерживается.
грамматика
canvas.toDataURL(type, encoderOptions);
параметр
- тип необязательный
Формат изображения, по умолчанию image/png
- encoderOptions необязательный
Если задан формат изображения image/jpeg или image/webp, качество изображения можно выбрать от 0 до 1. Если оно превышает диапазон значений, будет использоваться значение по умолчанию 0,92. Другие параметры игнорируются. Chrome поддерживает тип «image/webp».
догадка
возможноtoDataURL(type,quality)
Чем меньше второй параметр (качество), тем меньше размер файла
упражняться
Первый взгляд на исходный размер информации об изображении7.31kb
toDataURL(type,quality)
качество по умолчанию 0,92, чтобы увидеть, как результаты сжатия
<input id="fileInput" type="file" />
<img id="img" src="" alt="">
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 => {//compressImg方法见附录
console.log(res)
img.src = window.URL.createObjectURL(res.file);
})
}
compressImg方法见附录
Вы можете видеть, что размер изображения
8.83kb
После сжатия картинка становится крупнее, в чем дело?
Похоже, изначальная догадка не совсем верна, продолжим разбираться.
fileId.onchange = function (e) {
let file = e.target.files[0]
compressImg(file, 0.1).then(res => {//compressImg方法见附录
console.log(res)
img.src = window.URL.createObjectURL(res.file);
})
}
когда
quality
Когда он равен 0,1, изображение имеет только1.63kb
, то же качество падает
Продолжить... Много времени спустя
Давайте воспользуемся линейной диаграммой, чтобы увидеть ее более интуитивно.
Я сделал еще несколько снимков и позволил им использовать значение по умолчанию 0,92, и все они оказались больше оригинала.
Поэтому изображение, полученное при значении по умолчанию, часто больше исходного изображения.
Посмотрим, когдаquality
Насколько можно увеличить эффективность сжатия изображения
Максимальная эффективность сжатия, то есть максимальное сжатие без ущерба для качества изображения.
Попробовав серию картинок, я нашелкогдаquality
Между 0,2 и 0,5 качество картинки сильно не меняется.quality
Чем меньше значение, тем более впечатляющей является эффективность сжатия (то есть, когда она составляет около 0,2, сжатое изображение может быть максимально увеличено, не оказывая слишком большого влияния на качество изображения).
В заключение
Попрактиковавшись, можно сделать вывод, что изображение, получаемое при этом значении по умолчанию, часто бывает более высокого качества, чем исходное изображение.
когдаquality
Между 0,2 и 0,5 качество картинки сильно не меняется.quality
Чем меньше значение, тем более впечатляющей является эффективность сжатия (то есть, когда она составляет около 0,2, сжатое изображение может быть максимально увеличено, не оказывая слишком большого влияния на качество изображения).
приложение
/**
* 压缩方法
* @param {string} file 文件
* @param {Number} quality 0~1之间
*/
function compressImg(file, quality) {
if (file[0]) {
return Promise.all(Array.from(file).map(e => compressImg(e,
quality))) // 如果是 file 数组返回 Promise 数组
} else {
return new Promise((resolve) => {
const reader = new FileReader() // 创建 FileReader
reader.onload = ({
target: {
result: src
}
}) => {
const image = new Image() // 创建 img 元素
image.onload = async () => {
const canvas = document.createElement('canvas') // 创建 canvas 元素
canvas.width = image.width
canvas.height = image.height
canvas.getContext('2d').drawImage(image, 0, 0, image.width, image.height) // 绘制 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)
})
}
}
Оптимизация и обновление
При использовании обнаруживаются граничные проблемы, то есть размер изображения слишком велик, размер IOS ограничен, прозрачное изображение png становится черным и т. д.
Поэтому оптимизировано пропорциональное уменьшение изображений большого размера, что значительно повышает эффективность сжатия.
Для примера возьмем картинку размером 14M и размером 6016X4016.
Один14M
Исходное изображение (6016X4016) не меняет качество, а только меняет размер после сжатия (1400X935)139.62KB
Вполне возможно, что ограничение размера может максимизировать эффективность сжатия.
/**
* 压缩图片方法
* @param {file} file 文件
* @param {Number} quality 图片质量(取值0-1之间默认0.92)
*/
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))) // 如果是 file 数组返回 Promise 数组
} else {
return new Promise((resolve) => {
console.log(file)
if ((file.size / 1024).toFixed(2) < 300) {
resolve({
file: file
})
} else {
const reader = new FileReader() // 创建 FileReader
reader.onload = ({
target: {
result: src
}
}) => {
const image = new Image() // 创建 img 元素
image.onload = async() => {
const canvas = document.createElement('canvas') // 创建 canvas 元素
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
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
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
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
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) // 绘制 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)
}
})
}
},
напиши в конце
якрутой город а, фронтенд, любит технологии и любит жизнь.
Я очень счастлив встретить тебя.
Если вы хотите узнать больше, пожалуйста, нажмите здесь, с нетерпением жду вашего маленького ⭐⭐
-
Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, ставьте лайк и подписывайтесь 😊
-
Эта статья была впервые опубликована на Наггетс, перепечатка без разрешения запрещена 💌