Resize an image in a canvas element with js

6

Problem

Image resizing with JavaScript

Possible Solution

Use the canvas element to redraw the image, resize it and then render the image again.

Below are two use cases that are merely illustrative, because the sizes of the images will not be fixed like these, and will only be used to illustrate the orientation (portrait / landscape) of the image.

Case 1

This case uses Div of dimensions 851x315

Div:

Case1.1:

Resizeanimageofdimensions1080x1920toshowinthisdiv.

Image:

Case 1.2:

Resize an image of dimensions 1920x1080 to show in this div .

Image:

Case2

ThiscaseusesDivofdimensions500x450

Div:

Case 2.1:

Resize an image of dimensions 1080x1920 to show in this div .

Image:

Case2.2:

Resizeanimageofdimensions1920x1080toshowinthisdiv.

Image:

Logic used:

Resizing should not be done in order to model the image to fit the div , but rather maintain the proportionality of its dimensions, until one of them matches one 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 a canvas element to resize the image is that, shortly after the process, there is a large drop in image quality depending on its resolution.

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

After doing a lot of research on this, I found a post in SOen , which teaches you to do the resizing using a method called step-down , which in my understanding means step-

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 pretty much based on the post of the SOen , and this is functional for Case 2 quoted at the beginning of the post.

The image below is the original one:

Now,thesameimageappliedtodiv:

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 properly resized with the exception of Case 1 , also quoted at the beginning of post .

Using the same jaguar image, look at the result.

The image instead of resized, has been stretched totally. The problem may have to do with the number of steps to use, because the calculation is the result is 0 , that is, it is not to use any steps, but even omitting the steps and resizing direct, the result is the same.

I apologize if the post was too long, but I tried to be as clear as possible about my problem.

Any questions, or if something was missing, ask for the comments. :)

    
asked by anonymous 22.06.2015 / 01:20

1 answer

6

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

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

The example below places the image in <div> elements 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>
    
22.06.2015 / 15:50