0

I want to show four canvas from the same image

I'm working with an image which I need to be splitted into four pieces. I don't know the actual dimensions of the image so I need it to be dynamic. I already get this far, and I think the first piece is working fine, but I don't know why it is not working for the rest of the pieces. Could you point where the error could be?

I am new working with canvas, so my code is based on this answer: https://stackoverflow.com/a/8913024/6929416

var image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png';
image.onload = cutImageUp;

function cutImageUp() {

  var natW = image.width / 2;
  var natH = image.height / 2;

  var widthOfOnePiece = jQuery(window).width() / 2;
  var heightOfOnePiece = jQuery(window).height() / 2;

  var imagePieces = [];
  for (var x = 0; x < 2; ++x) {
    for (var y = 0; y < 2; ++y) {
      var canvas = document.createElement('canvas');
      canvas.width = widthOfOnePiece;
      canvas.height = heightOfOnePiece;
      var context = canvas.getContext('2d');
      context.drawImage(image,
        x * natW, y * natH,
        natW, natH,
        x * canvas.width, y * canvas.height,
        canvas.width, canvas.height
      );
      /*drawImage(image,
       sx, sy,
       sWidth, sHeight,
       dx, dy,
       dWidth, dHeight);*/
      imagePieces.push(canvas.toDataURL());
    }
  }

  // imagePieces now contains data urls of all the pieces of the image

  // load one piece onto the page
  var anImageElement = document.getElementById('testing');
  var anImageElement2 = document.getElementById('testing2');
  var anImageElement3 = document.getElementById('testing3');
  var anImageElement4 = document.getElementById('testing4');

  anImageElement.src = imagePieces[0];
  anImageElement2.src = imagePieces[1];
  anImageElement3.src = imagePieces[2];
  anImageElement4.src = imagePieces[3];
}
img{ border: 1px solid; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<img id="testing" src="">
<img id="testing2" src="">
<img id="testing3" src="">
<img id="testing4" src="">
</section>

I expect the canvas dimensions to fit on the screen, so I set them to half of the windows width and height.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • what do you need here ? what are the errors ? If you could explain that little bit more. – cantona_7 May 08 '19 at 06:57
  • Only the first canvas is being shown, the rest of them are taking data but apparently it is not the right data or I am not clipping the original image right. I need the anothers canvas to show the rest of the image: First piece: top left, Second piece: top right, Third piece: bottom left, Fourth piece: bottom right .... showing the whole image but splitted into four pieces. – rfgonzalezweb May 08 '19 at 07:05
  • Can you add the complete code (html). – cantona_7 May 08 '19 at 07:11
  • Sure, html added – rfgonzalezweb May 08 '19 at 07:19
  • where is your canvas ? something like ``. I think you should check this out `mdn canvasdrawImage` – cantona_7 May 08 '19 at 07:42

1 Answers1

1

The parameters for drawImage are

drawImage(
  source,
  sourceX, sourceY, sourceWidth, sourceHeight,
  destinationX, destinationY, destinationWidth, destinationHeight
)

In your code, you are setting destinationX to x * canvas.width and destinationY to y * canvas.height. This means that for every iteration where either x or y are not 0, you are drawing outside of the destination area, that is for every parts but the first one.

Simply hard-code destinationX-Y to 0and you'll be good.

var image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png';
image.onload = cutImageUp;

function cutImageUp() {

  var natW = image.width / 2;
  var natH = image.height / 2;

  var widthOfOnePiece = jQuery(window).width() / 2;
  var heightOfOnePiece = jQuery(window).height() / 2;

  var imagePieces = [];
  for (var x = 0; x < 2; ++x) {
    for (var y = 0; y < 2; ++y) {
      var canvas = document.createElement('canvas');
      canvas.width = widthOfOnePiece;
      canvas.height = heightOfOnePiece;
      var context = canvas.getContext('2d');
      context.drawImage(image,
        x * natW, y * natH,
        natW, natH,
        0, 0,
        canvas.width, canvas.height
      );
      /*drawImage(image,
       sx, sy,
       sWidth, sHeight,
       dx, dy,
       dWidth, dHeight);*/
      imagePieces.push(canvas.toDataURL());
    }
  }

  // imagePieces now contains data urls of all the pieces of the image

  // load one piece onto the page
  var anImageElement = document.getElementById('testing');
  var anImageElement2 = document.getElementById('testing2');
  var anImageElement3 = document.getElementById('testing3');
  var anImageElement4 = document.getElementById('testing4');

  anImageElement.src = imagePieces[0];
  anImageElement2.src = imagePieces[1];
  anImageElement3.src = imagePieces[2];
  anImageElement4.src = imagePieces[3];
}
img{ border: 1px solid; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
  <img id="testing" src="">
  <img id="testing2" src="">
  <img id="testing3" src="">
  <img id="testing4" src="">
</section>

But note that toDataURL should almost never be used. Instead one should always prefer faster non-blocking and memory friendly toBlob().
Also, no need to create 4 canvases here, you can reuse the same at every round.

And finally, you might want to preserve your image's aspect ratio:

var image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png';
image.onload = cutImageUp;

function cutImageUp() {

  var natW = image.width / 2;
  var natH = image.height / 2;

  var widthOfOnePiece = jQuery(window).width() / 2 - 20;
  // preserve aspect ratio
  var heightOfOnePiece = widthOfOnePiece * (natH / natW);

  var canvas = document.createElement('canvas');
  canvas.width = widthOfOnePiece;
  canvas.height = heightOfOnePiece;
  var context = canvas.getContext('2d');
  var promises = [];
  for (var y = 0; y < 2; ++y) {
    for (var x = 0; x < 2; ++x) {
      context.clearRect(0, 0, canvas.width, canvas.height);
      context.drawImage(image,
        x * natW, y * natH,
        natW, natH,
        0, 0,
        canvas.width, canvas.height
      );
      promises.push(new Promise(function(resolve, reject) {
        canvas.toBlob(function(blob) {
          if (!blob) reject();
          resolve(blob);
        });
      }));
    }
  }

  return Promise.all(promises).then(function(blobs) {
    // imagePieces now contains data urls of all the pieces of the image

    // load one piece onto the page
    var anImageElement = document.getElementById('testing');
    var anImageElement2 = document.getElementById('testing2');
    var anImageElement3 = document.getElementById('testing3');
    var anImageElement4 = document.getElementById('testing4');

    anImageElement.src = URL.createObjectURL(blobs[0]);
    anImageElement2.src = URL.createObjectURL(blobs[1]);
    anImageElement3.src = URL.createObjectURL(blobs[2]);
    anImageElement4.src = URL.createObjectURL(blobs[3]);
  })
}
img {
  border: 1px solid;
  display: inline-block;
}
body{margin: 0;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
  <img id="testing" src="">
  <img id="testing2" src="">
  <img id="testing3" src="">
  <img id="testing4" src="">
</section>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Sir you just saved my day, I had been trying to make it work for days. Thank you so much. Any trick to achieve the aspect ratio preserve? or should I just improve my code? – rfgonzalezweb May 08 '19 at 20:30