Во фронтенде реализована функция сжатия картинок

JavaScript
Во фронтенде реализована функция сжатия картинок
Зачем использовать интерфейс для сжатия изображений

В последнее время я работаю над функцией h5 мобильного терминала для загрузки изображений.Изначально эта функция не сложна.Его нужно только передать файл изображения на сервер через axios.Однако, учитывая, что функция камеры мобильного телефона очень мощный, просто сделайте снимок. Он легко может быть пять или шесть мегабайт, а требование сервера состоит в том, что загружаемое изображение должно быть меньше двух мегабайт, а при прямой загрузке такого большого изображения пропускная способность недопустима, поэтому фронт- конец сжатия изображения становится необходимой ссылкой.

эффект сжатия


Сначала представим общий процесс сжатия

  1. Получите файл изображения для загрузки через собственный тег ввода
  2. Преобразование файлов изображений в теги элементов img
  3. Нарисуйте этот HTMLImageElement, сжатый на холсте
  4. Преобразование изображения, нарисованного холстом, в файл BLOB-объектов.
  5. Наконец, отправьте файл BLOB-объектов на сервер.
  6. Заканчивать!

Далее смотрите подробные шаги

Учитывая полноту статьи и шагов, я напишу каждую деталь, даже если некоторые вещи будут очень простыми.

1. Используйте тег Input для получения ресурсов файла изображения.

Этот шаг должен быть наиболее знаком всем, собственный тег ввода, установивtypeАтрибут file позволяет пользователю выбрать файл, установитьacceptОграничьте выбранный тип файла и привяжите событие onchange, чтобы получить выбранный файл

<input type="file" accept="image/*" />

Щелкните элемент управления, активируйте фокус, откройте проводник, выберите файл и подтвердите, событие изменения будет запущено, поэтому вы можете получить выбранный файл в обратном вызове события изменения, это выглядит так


2. Прочитайте файл и преобразуйте его в элемент тега img.

Получив файл изображения, сначала преобразуйте его в HTMLImageElement, который является обычным тегом img.Конструктор FileReader.

Сначала создайте экземпляр img и fileReader через файлReaderAPI readAsDataURL используется для чтения файла изображения, и его возвращаемое значение представляет собой закодированную строку base64, а затем присваивает эту строку атрибуту src для img, тем самым завершая файл изображения дляПреобразование HTMLImageElement.

// 先new一个img和fileReader的实例
const img = new Image()
const reader = new FileReader()// 读取文件资源
reader.readAsDataURL(file)  
reader.onload = function(e){ 
  img.src = e.target.result
}

Преобразованный HTMLImageElement


3. сжатие холста, основные шаги

После получения преобразованного элемента img сначала извлеките ширину и высоту элемента, которые являются шириной и высотой фактического файла изображения.

const { width: originWidth, height: originHeight } = img

Затем определите максимальную ширину и высоту, если они превышают эту предельную ширину и высоту, они будут масштабироваться пропорционально

 // 最大尺寸限制
 const maxWidth = 1000,maxHeihgt = 1000
 // 需要压缩的目标尺寸
 let targetWidth = originWidth, targetHeight = originHeight
 // 等比例计算超过最大限制时缩放后的图片尺寸
 if (originWidth > maxWidth || originHeight > maxHeight) {
      if (originWidth / originHeight > 1) {
        //  宽图片
        targetWidth = maxWidth
        targetHeight = Math.round(maxWidth * (originHeight / originWidth))
      } else {
        // 高图片
        targetHeight = maxHeight
        targetWidth = Math.round(maxHeight * (originWidth / originHeight))
      }
   }

После расчета размера для сжатия создайте экземпляр холста, установите ширину и высоту холста на рассчитанный размер после сжатия и нарисуйте на нем img.

// 创建画布
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')

// 设置宽高度为等同于要压缩图片的尺寸
 canvas.width = targetWidth
 canvas.height = targetHeight
 context.clearRect(0, 0, targetWidth, targetHeight)
 //将img绘制到画布上
 context.drawImage(img, 0, 0, targetWidth, targetHeight)

4. Преобразовать в файл BLOB-объектов

После того, как холст нарисован, вы можете использоватьtoBlob для преобразования изображения в файл большого двоичного объекта, этот API принимает три входных параметра.

canvas.toBlob(callback, type, encoderOptions);

Преобразованный файл BLOB-объекта можно получить в функции обратного вызова.Тип — это тип изображения, которое нужно преобразовать, по умолчанию — png.

encoderOptions — это когда заданный формат изображенияimage/jpegилиimage/webpиспользуется для указания качества отображения изображения.

Так что если мы просто хотим сжать картинки в формате jpg или webp, нам не нужно выполнять третью операцию, просто используем этот апи напрямую, а затем заполняем нужные параметры качества. Но на самом деле нам еще предстоит рассмотреть множество форматов изображений, поэтому необходимо использовать третий процесс.

Преобразованный блоб выглядит так


5. Загрузите большой двоичный объект, и все готово.

полная реализация кода

Поскольку во всем процессе есть асинхронные операции обратного вызова, я использую асинхронность для достижения синхронного выполнения асинхронного кода.

// 压缩前将file转换成img对象
function readImg(file)  {
  return new Promise((resolve, reject) => {
    const img = new Image()
    const reader = new FileReader()
    reader.onload = function(e) {
      img.src = e.target.result
    }
    reader.onerror = function(e) {
      reject(e)
    }
    reader.readAsDataURL(file)
    img.onload = function() {
      resolve(img)
    }
    img.onerror = function(e) {
      reject(e)
    }
  })
}

/**
 * 压缩图片
 *@param img 被压缩的img对象
 * @param type 压缩后转换的文件类型
 * @param mx 触发压缩的图片最大宽度限制
 * @param mh 触发压缩的图片最大高度限制
 */
function compressImg(img, type, mx, mh) {
  return new Promise((resolve, reject) => {
    const canvas = document.createElement('canvas')
    const context = canvas.getContext('2d')
    const { width: originWidth, height: originHeight } = img
    // 最大尺寸限制
    const maxWidth = mx
    const maxHeight = mh
    // 目标尺寸
    let targetWidth = originWidth
    let targetHeight = originHeight
    if (originWidth > maxWidth || originHeight > maxHeight) {
      if (originWidth / originHeight > 1) {
        //  宽图片
        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(img, 0, 0, targetWidth, targetHeight)
    canvas.toBlob(function(blob) {
      resolve(blob)
    }, type || 'image/png')  })
}

Общий процесс выполнения может быть изменен в соответствии с вашими потребностями.

async function upload(file){
   const img = await readImg(file)
   const blob = await compressImg(img, file.type, 1000, 1000)
   const formData = new FormData()
   formData.append('file', blob, 'xxx.jpg')
    axios.post('http://xxx.com/api',formData)
}
upload(file).catch(e => console.log(e))


наконец

Спасибо за чтение, я надеюсь добиться прогресса вместе с вами, если вы найдете это полезным, пожалуйста, поставьте лайк 😁😁