Как выборочно убрать пустую область с картинки с помощью JavaScript?

Есть png изображение с пустой областью по краям (на скриншоте пустая область - это клеточки)

Можно ли как-то сделать так, чтобы область сверху и снизу обрезалась, а по краям осталась?

Пока есть вариант пробегаться по каждому пикселю, если весь ряд пустой, то удаляем. Но может есть более простой способ?


Ответы (1 шт):

Автор решения: Daniil Loban

Вот пример кода, к сожалению сниппет создать не удалось, но код работает относительно быстро, за счет метода массива some. Строка которая появилась сверху это новое изображение на канвасе нарисованное поверх оригинального (600x600 со словом "пример" в центре).

пример работы кода

  • loadImage загужает изображение

  • getData получает пиксельные данные (по четыре байта на пискель rgba) следует учесть что иногда данные изображения получить нельзя из-за того что оно будет находится в другом домене можно попробовать установить аттрибут img.setAttribute('crossOrigin', '')

  • в строке line = data.data.slice(i, i + blockSize) мы вырезаем линию из изображения

  • (line.some(byte => byte !== 0)) проверяет что она не полностью пуста (иначе ни данных о цвете ни о прозрачности - все нули).

  • зная начальную и конечные линии "непустого" изображения можем вырезать его ctx.getImageData(0, top, width , newHeight);.

  • и отрисовать новое изображение ctx.putImageData(trimmedImage, 0, 0)

function loadImage (imageUrl){
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = function() {
      resolve({img, width: this.width, height : this.height})
    }
    img.src = imageUrl;
  })
}

function getData(ctx, width, height){
  try {
    return ctx.getImageData(0, 0, width, height);
  } catch(e) {
    return null; // ошибка получения данных
  }
}

function trimTransparent(ctx, width, height){
  const blockSize = width * 4
  const data = getData(ctx, width, height)
  if (!data) return null
  const length = data.data.length;
  const filledLines = [];
  let i = 0;
  let lineNumber = 0 
  while (i < length) {
    lineNumber += 1
    line = data.data.slice(i, i + blockSize)
    if (line.some(byte => byte !== 0)) {
      filledLines.push(lineNumber)
    }
    i += blockSize  
  }

  const top = filledLines.shift()
  const newHeight = filledLines.pop() - top
  return ctx.getImageData(0, top, width , newHeight);
} 

async function trimImage(imageUrl){
  const {img, width, height} = await loadImage(imageUrl)
  // можно создавать временный канвас вне DOM
  const canvas = document.querySelector('canvas');
  canvas.width = width;
  canvas.height = height;
  const ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0, width, height);
  const trimmedImage = trimTransparent(ctx, width, height)
  if (trimmedImage){
    ctx.putImageData(trimmedImage, 0, 0) 
  }
}

trimImage("example.png")
<canvas></canvas>

→ Ссылка