0

When learning about HTML canvas, I found this

Example: Dynamically change images

Basically dynamically change image from colors to gray-scale and vice versa using HTML Canvas. This example select img elements by class names ('grayscale') so to be able to apply this process to multiple images at once.

HTML

<img class="grayscale" src="myPicture.png" alt="Description of my picture" />

JavaScript

window.addEventListener('load', removeColors);

function showColorImg() {
  this.style.display = 'none';
  this.nextSibling.style.display = 'inline';
}

function showGrayImg() {
  this.previousSibling.style.display = 'inline';
  this.style.display = 'none';
}

function removeColors() {
  var aImages = document.getElementsByClassName('grayscale'),
      nImgsLen = aImages.length,
      oCanvas = document.createElement('canvas'),
      oCtx = oCanvas.getContext('2d');
  for (var nWidth, nHeight, oImgData, oGrayImg, nPixel, aPix, nPixLen, nImgId = 0; nImgId < nImgsLen; nImgId++) {
    oColorImg = aImages[nImgId];
    nWidth = oColorImg.offsetWidth;
    nHeight = oColorImg.offsetHeight;
    oCanvas.width = nWidth;
    oCanvas.height = nHeight;
    oCtx.drawImage(oColorImg, 0, 0);
    oImgData = oCtx.getImageData(0, 0, nWidth, nHeight);
    aPix = oImgData.data;
    nPixLen = aPix.length;
    for (nPixel = 0; nPixel < nPixLen; nPixel += 4) {
      aPix[nPixel + 2] = aPix[nPixel + 1] = aPix[nPixel] = (aPix[nPixel] + aPix[nPixel + 1] + aPix[nPixel + 2]) / 3;
    }
    oCtx.putImageData(oImgData, 0, 0);
    oGrayImg = new Image();
    oGrayImg.src = oCanvas.toDataURL();
    oGrayImg.onmouseover = showColorImg;
    oColorImg.onmouseout = showGrayImg;
    oCtx.clearRect(0, 0, nWidth, nHeight);
    oColorImg.style.display = "none";
    oColorImg.parentNode.insertBefore(oGrayImg, oColorImg);
  }
}

So far so good, the example works well except that when I use a big image, it makes the horizontal scroll bar appears (image width is 2400px) so I've opted to precise the width of the image in the img tag as in here:

<img class="grayscale" src="mypic3.jpg" width="698px" alt="Description of my picture" />

So that also works for the original colorful image, but for the modified one (gray-scale one) it seems to map only a portion of the image to the designed width. so instead of getting this image:the full image gray-scaled

I'm getting only this one :

only a portion gray-scaled of the original image

Notice that this happens even if oCanvas.width = nWidth;initialize canvas width with the offset of the img tag (that is 689)

Other useless things that I've tried :

oCtx.drawImage(oColorImg, 0, 0);
oCanvas.style.backgroundSize="70%";
oCanvas.style.width="689px";
oCtx.putImageData(oImgData, 0, 0, 0, 0, 698, 410);

I appreciate any help

Edit: for the marks as duplicate - For the one who marked the question as duplicate of question answered by just setting the canvas width, I specifically declared that it didn't work and the code above include setting canvas width.
- For the last four ones, which use the extended form of drawimage(), I did try it but it didn't work unless I set an explicit global CSS rule setting the img tag width. So I'm not sure what is the mechanics behind that. But in this case, if I don't put this rule the problem persist. So in my humble opinion, adding this particular information my help people stuck in a similar problem. as well as any explanation on why or how this works. thanks

user10089632
  • 5,216
  • 1
  • 26
  • 34
  • [`ctx.drawImage(Image, 0, 0);`](https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/drawImage) will draw your image at the coords 0, 0 and at size Image.naturalWidth, Image.naturalHeight. What you want is probably `ctx.drawImage(Image, 0, 0, canvas.width, canvas.height);` so that your image gets resized to the size of your canvas. – Kaiido Aug 10 '17 at 07:22
  • @Kaiido, yeah, except two things, I'm using putImageData because I don't have a canvas element, and I've tried the extended format of it right at the bottom of the post – user10089632 Aug 10 '17 at 07:25
  • but before using putImageData, you are using getImageData, which is itself called after you did call `oCtx.drawImage(oColorImg, 0, 0);` – Kaiido Aug 10 '17 at 07:26
  • @Kaiido, yeah oops, forget to mention that didn't work neither – user10089632 Aug 10 '17 at 07:30
  • You will have to write the image data to a temporary canvas thats sized the natural size of the image (`imageData.width` and `.height`), and then draw that canvas into your final image, as it can be resized using `drawImage( 0, 0, canvas.width, canvas.height)`. – somethinghere Aug 10 '17 at 07:42
  • 1
    https://jsfiddle.net/qujgfmmL/ I only changed the image to an CORS ok one, and `oCtx.drawImage(oColorImg, 0, 0);` to `oCtx.drawImage(oColorImg, 0, 0, nWidth, nHeight);` which is what I told you to do. – Kaiido Aug 10 '17 at 07:44
  • @Kaiido, that works but only with the addition of the global CSS rule about image width, please put your answer with this two changes so I can accept it, thank you – user10089632 Aug 10 '17 at 07:50
  • @somethinghere, I don't know, may be that works, but I think Kaiido answer is more straight forward, thanks – user10089632 Aug 10 '17 at 07:56

1 Answers1

0

The answer consists of two steps :

  1. The first (as Kaiido suggests) is making use of the extended syntax of drawImage() taking the canvas.width,canvas.height parameters into account

    oCtx.drawImage(oColorImg, 0, 0, nWidth, nHeight);

  2. The second is that you make a CSS rule setting images width (notice that if you don't, the problem persists even if the image seems to have the width set normally both in display and by extracting image width with Javascript. So one way to improve this answer is to explain this strange behavior)

    img{ width: 689px; }

user10089632
  • 5,216
  • 1
  • 26
  • 34