Resize an image in a canvas element with js

Asked

Viewed 5,665 times

6

Problem

Image resizing with Javascript

Possible solution

Use the widget canvas to redesign the image, resize it and then render the image again.

Below will be cited 2 cases of use merely illustrative, as the sizes of the images will not be fixed like these, being used only to illustrate the orientation (portrait/landscape) of the image.

Case 1

This case uses the Div of dimensions 851x315

Div:

Div 851x315

Case 1.1:

Resize an image of dimensions 1080x1920 for presentation in this div.

Imagery:

Imagem 1080x1920

Case 1.2:

Resize an image of dimensions 1920x1080 for presentation in this div.

Imagery:

Imagem 1920x1080

Case 2

This case uses the Div of dimensions 500x450

Div:

Div 500x450

Case 2.1:

Resize an image of dimensions 1080x1920 for presentation in this div.

Imagery:

Imagem 1080x1920

Case 2.2:

Resize an image of dimensions 1920x1080 for presentation in this div.

Imagery:

Imagem 1920x1080

Logic used:

Resizing should not be done in order to model the image to fit the div, but maintaining the proportionality of its dimensions, until one of these equals to one of the dimensions of div.

The code block below is self explanatory:

if (alturaImagem <= larguraImagem) {
    proporcao = alturaDiv / alturaImagem;
    novaLarguraImagem = larguraImagem * proporcao;
    novaAlturaImagem = alturaDiv;
} else {
    proporcao = larguraDiv / larguraImagem;
    novaLarguraImagem = larguraDiv;
    novaAlturaImagem = alturaImagem * proporcao;
}

The problem of using this code and an element canvas to resize the image is that, soon after the process, there is a big drop in the image quality, depending on its resolution.

If it is an image of dimensions close to the div, almost no drop in quality, but if the difference is too big (example: resize an image of dimensions 7680x4320 to a div 851x315) image quality drops too much and this is very apparent.

After doing a lot of research on this, I found a post in the Soen, which teaches how to resize using a method called step-down, which to my mind means step-by-step reduction (correct me otherwise), there is a formula to calculate the number of steps that will be used, as explained in post linked.

My code:

var file, img, width, height, ratio, nWidth, nHeight;
var _URL = (window.URL) ? window.URL : window.webkitURL;

if ((file = e.target.files[0])) {
    img = new Image();
    img.src = _URL.createObjectURL(file);
    img.onload = function () {
        width = this.width;
        height = this.height;

        // Criação do primeiro elemento canvas

        var canvas = document.createElement("canvas");
        var ctx = canvas.getContext("2d");

        // Altura e largura da div

        tWidth = $("div#mp-change-bg").width();
        tHeight = $("div#mp-change-bg").height();

        // Criação do segundo elemento canvas, que será manipulado off-screen

        var oc = document.createElement("canvas");
        var octx = oc.getContext("2d");

        oc.width = width * 0.5;
        oc.height = height * 0.5;

        // 1º passo

        octx.drawImage(this, 0, 0, oc.width, oc.height);

        // 2º passo

        octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);

        // Definição das novas dimensões da imagem

        if (height <= width) {
            ratio = tHeight / height;
            canvas.width = width * ratio;
            canvas.height = tHeight;
        } else {
            ratio = tWidth / width;
            canvas.width = tWidth;
            canvas.height = height * ratio;
        }

        // 3º e último passo

        ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5, 0, 0, canvas.width, canvas.height);

        $("img#mp-image-bg").attr("src", canvas.toDataURL("image/png")).css("display", "block");
    };
}

I made the code practically based on the answer given in post of Soen, and this is functional for the Case 2 quoted at the beginning of the post.

The image below is the original:

inserir a descrição da imagem aqui

Now, the same image applied to div:

Onça na div

As you can see, I’m using a plugin to reposition the image in div, with drag and drop. Regardless of the orientation of the image used, it is resized correctly, with the exception of Case 1, also quoted at the beginning of post.

Using the same jaguar image, look at the result.

inserir a descrição da imagem aqui

The image, instead of being resized, was fully stretched. The problem may have to do with the number of steps to use, because through the calculation, the result found is 0, that is, it is not to use any step, but even omitting the steps and resizing directly, the result is the same.

I apologize if the post was very extensive, but I tried to be as clear as possible exposing my problem.

Any questions, or if anything is missing, ask in the comments. :)

1 answer

6


I considered for the answer that the image should fill the whole space.

The code shown checks only the ratio of the original image. I modified the code to check the aspect ratio of the image and the <div> and recalculate the "cut" positions of .drawImage().

The example below puts the image in elements <div> of various sizes.

(function(){
  var image = new Image();
  image.addEventListener('load', function(){
    var divs = document.querySelectorAll('div')
    // tamanho original
    , oWidth = this.width
    , oHeight = this.height;
    for (var i = 0; i < divs.length; i++) {
      var canvas = document.createElement('canvas')
      ,   ctx = canvas.getContext('2d')
      // coordenadas origem (source)
      ,   sx = 0
      ,   sy = 0
      ,   sWidth = oWidth
      ,   sHeight = oHeight
      // tamanho destino
      ,   dWidth = divs[i].offsetWidth
      ,   dHeight = divs[i].offsetHeight
      // tamanho ideal
      ,   iWidth = Math.round(sHeight / dHeight * dWidth)
      ,   iHeight = Math.round(sWidth / dWidth * dHeight);
      if (sWidth > iWidth) { // cortar na largura
        sx = parseInt((sWidth - iWidth) / 2);
        sWidth = iWidth;
      } else if (sHeight > iHeight) { // cortar na altura
        sy = parseInt((sHeight - iHeight) / 2);
        sHeight = iHeight;
      }
      canvas.width = dWidth;
      canvas.height = dHeight;
      ctx.drawImage(this, sx, sy, sWidth, sHeight, 0, 0, dWidth, dHeight);
      divs[i].appendChild(canvas);
    }
  }, false);
  image.src = 'http://i.stack.imgur.com/bY8Iy.png';
})();
div {
	margin: 5px;
	display: inline-block;
}
<div style="width: 200px; height: 300px;"></div>
<div style="width: 100px; height: 300px;"></div>
<div style="width: 315px; height: 200px;"></div>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.