Resize an image on a canvas element with js

Problem

Image resizing with JavaScript

Possible solution

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

Below will be cited 2 use cases merely illustrative, because 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.

Image:

1080x1920 picture

Case 1.2:

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

Image:

1920x1080 picture

Case 2

This case uses Div of dimensions 500x450

Div:

Div 500x450

Case 2.1:

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

Image:

1080x1920 picture

Case 2.2:

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

Image:

1920x1080 picture

Logic used:

Resizing should not be done with in order to model the image to fit the div, but keeping the proportionality of its dimensions, until one of these equals one of the dimensions of the 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 with using this code and a Canvas element to resize the image is that, right after the process, there is a big drop in image quality depending on its resolution.

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

After doing several research on this, I found a post in SOen, which teaches you how to resize using a method called step-down, that in my opinion means Step-by-step reduction (correct me if not), there is a formula to calculate the number of steps that will be used, as explained in the 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 pretty much based on the answer that was given in the post of the SOen, and this is functional for the Case 2 cited at the beginning of the post.

The image below is the original:

insert the description of the image here

Now the same image applied to div :

Ounce in div

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

Using the same image of the ounce, look at the result.

insert the description of the image here

The image was stretched completely instead of resized. We 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 direct, the result is the same.

I apologize if the post got too extensive, but I tried to be as clear as possible by exposing my problem.

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

Author: Comunidade, 2015-06-21

1 answers

I considered for the answer that the image should fill the entire 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>
 6
Author: Pedro Sanção, 2015-06-22 13:50:33