Canvas Rotate, toDataUrl, а затем Crop портит качество изображения - PullRequest
0 голосов
/ 07 марта 2019

У меня есть изображение, которое я позволяю пользователям поворачивать на 90 градусов в любом направлении. Каждый раз, когда они вращаются, я использую canvas для выполнения манипуляций с изображениями, а затем сохраняю данные, возвращаемые canvas.toDataURL("image/png", 1).

Проблема в том, что качество изображения уменьшается при каждом повороте изображения.

Моя конечная цель - вращать прямоугольное изображение без потери качества изображения, а также сохранять новый URL-адрес данных.

function rotateAndSave(image: HTMLImageElement, degrees: number): string {
  const imageWidth = image.naturalWidth;
  const imageHeight = image.naturalHeight;
  const startedHorizontalEndedVertical = imageWidth > imageHeight;
  const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;

  const canvas = document.createElement("canvas");
  canvas.width = canvasSize;
  canvas.height = canvasSize;

  const ctx = canvas.getContext("2d");
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // center and rotate canvas
  const translateCanvas = canvasSize / 2;
  ctx.translate(translateCanvas, translateCanvas);
  ctx.rotate(degrees * Math.PI / 180);

  // draw from center
  const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
  const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
  ctx.drawImage(image, translateImageX, translateImageY); 

  // I got 'cropPlusExport' from another stackoverflow question.
  function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight) {
    // create a temporary canvas sized to the cropped size
    const canvas1 = document.createElement('canvas');
    canvas1.width = cropWidth;
    canvas1.height = cropHeight;
    const ctx1 = canvas1.getContext('2d');
    ctx1.setTransform(1, 0, 0, 1, 0, 0);
    ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
    // use the extended from of drawImage to draw the
    // cropped area to the temp canvas
    ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
    return canvas1.toDataURL("image/png", 1);
  }

  // Start Cropping
  let squareImage = new Image();
  squareImage.src = canvas.toDataURL("image/png", 1);
  squareImage.onload = () => {
    const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
    const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
    const sw = imageHeight;
    const sh = imageWidth;
    const data = cropPlusExport(squareImage, sx, sy, sw, sh);

    // Update DOM via angular binding...
    const dataUrl = data.split(",")[1];
    this.imageSource = dataUrl;

    squareImage = null;
  }

пример HTML

<div class="view">
  <img [src]="imageSource" />
</div>

Имейте в виду, что я обрезаю до естественной ширины и высоты изображения. Итак, что странно, если я не обрезаю, то качество изображения не меняется, но когда я обрезаю, качество изображения меняется.

1 Ответ

1 голос
/ 07 марта 2019

Рисование на холсте с потерями, а поворот изображения вызывает жесткие изменения пикселей.Таким образом, если вы начнете всегда с последнего состояния, вы добавите к своему изображению все больше и больше артефактов.

Просто сохраните оригинальное изображение где-нибудь и всегда начинайте с него, а не используйте модифицированную версию.

// will fire in a loop
img.onload = e => elem.rotateAndSave(1);

const elem = {
  // store a copy of the original image
  originalimage: img.cloneNode(),
  angle: 0,
  rotateAndSave(degrees) {
    // always use the stored original image
    const image = this.originalimage;
    // keep track of current transform
    this.angle += degrees;
    const imageWidth = image.naturalWidth;
    const imageHeight = image.naturalHeight;
    const startedHorizontalEndedVertical = imageWidth > imageHeight;
    const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;

    const canvas = document.createElement("canvas");
    canvas.width = canvasSize;
    canvas.height = canvasSize;

    const ctx = canvas.getContext("2d");
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // center and rotate canvas
    const translateCanvas = canvasSize / 2;
    ctx.translate(translateCanvas, translateCanvas);
    ctx.rotate(this.angle * Math.PI / 180);

    // draw from center
    const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
    const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
    ctx.drawImage(image, translateImageX, translateImageY);

    // I got 'cropPlusExport' from another stackoverflow question.
    function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight) {
      // create a temporary canvas sized to the cropped size
      const canvas1 = document.createElement('canvas');
      canvas1.width = cropWidth;
      canvas1.height = cropHeight;
      const ctx1 = canvas1.getContext('2d');
      ctx1.setTransform(1, 0, 0, 1, 0, 0);
      ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
      // use the extended from of drawImage to draw the
      // cropped area to the temp canvas
      ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
      return canvas1.toDataURL("image/png", 1);
    }

    // Start Cropping
    let squareImage = new Image();
    squareImage.src = canvas.toDataURL("image/png", 1);
    squareImage.onload = () => {
      const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
      const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
      const sw = imageHeight;
      const sh = imageWidth;
      const data = cropPlusExport(squareImage, sx, sy, sw, sh);

      // Update DOM via angular binding...
      img.src = data;
    }
  }
};
<img crossorigin src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg" id="img">
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...