Компрессор изображений МБ → КБ с использованием Canvas и JS: конечный файл остается больше целевого размераJavascript

Форум по Javascript
Ответить
Anonymous
 Компрессор изображений МБ → КБ с использованием Canvas и JS: конечный файл остается больше целевого размера

Сообщение Anonymous »

Я создаю клиентский компрессор изображений MB → KB, который использует HTML5 и Canvas.toBlob() для сжатия изображений в браузере. Я использую подход двоичного поиска по качеству JPEG, чтобы достичь заданного пользователем целевого размера (КБ). Для многих изображений это работает, но для некоторых (особенно PNG или больших фотографий) полученный сжатый файл все равно остается намного больше целевого КБ, даже если качество установлено очень низкое.
Что я хочу

Сжимать загружаемые изображения в браузере до размера ≤ целевого КБ (например, 100 КБ), сохраняя при этом достойное визуальное качество.
Ожидаемое поведение

Когда я устанавливаю целевой размер = 100 КБ, размер загруженного файла должен быть ≤ 100 КБ.
Фактическое поведение

Результат остается значительно большим (например, 300–600 КБ). Снижение качества JPEG до 0,05 иногда помогает, но для некоторых изображений этого недостаточно. Выходные файлы в формате PNG остаются большими.
Шаги по воспроизведению
  • Откройте страницу (пример: https://mbtokbconverter.org/ или вставьте локальную демонстрацию).
  • Загрузите большой PNG или фотографию с высоким разрешением (~2–5 МБ).
  • Установите целевой размер: 100 КБ → нажмите Сжать.
  • Наблюдайте за размером выходного файла.
Среда
  • Браузер: Chrome / Firefox (последняя стабильная версия)
  • ОС: Windows 10/macOS
  • Код выполняется на стороне клиента (без сервера)
    Соответствующий код (упрощенный)
HTML (вводы/кнопки)

Код: Выделить всё


 
Compress
Download

JS (основная логика сжатия)

Код: Выделить всё

function readFileAsDataURL(file){
return new Promise((resolve,reject)=>{
const fr = new FileReader();
fr.onload = () => resolve(fr.result);
fr.onerror = () => reject(fr.error);
fr.readAsDataURL(file);
});
}

function createImage(url){
return new Promise((resolve,reject)=>{
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error('Image load error'));
img.src = url;
});
}

function canvasToBlob(canvas, type, quality){
return new Promise(resolve => canvas.toBlob(resolve, type, quality));
}

async function compressImage(file, targetKB, maxWidth){
const targetBytes = targetKB * 1024;
const dataUrl = await readFileAsDataURL(file);
const img = await createImage(dataUrl);

let w = img.width, h = img.height;
if(maxWidth > 0 && maxWidth < w){
h = Math.round(h * (maxWidth / w));
w = maxWidth;
}

const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
canvas.getContext('2d').drawImage(img, 0, 0, w, h);

// binary-search on quality
let low = 0.05, high = 0.95, bestBlob = null;
for(let i = 0; i < 10; i++){
const q = (low + high) / 2;
const blob = await canvasToBlob(canvas, 'image/jpeg', q);
if(!blob) break;
if(blob.size 

Подробнее здесь: [url]https://stackoverflow.com/questions/79824623/mb-%e2%86%92-kb-image-compressor-using-canvas-and-js-final-file-remains-larger-than-tar[/url]
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»