1

I used canvas to change the color (this case to grayscale) of the images on a web page. However, only the last image is changed, by using the following code.

But I want to CHANGE THE COLOR of ALL IMAGES on a WEB PAGE

<!doctype html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<script>
$(function(){

    var theImages = [];
    var myImages = [];
    var theChanged = [];

    theImages = document.getElementsByTagName("img");
    theChanged = document.getElementsByTagName("img");

    // create a temporary canvas to put the original image
    var tempCanvas=document.createElement("canvas");
    var tempCtx=tempCanvas.getContext("2d");

    for (var i=0; i <theImages.length; i++){

                    myImages[i] = theImages [i];
                    tempCtx.drawImage(myImages[i],myImages[i].width,myImages[i].height);

                    var newImage=new Image();
                    newImage.onload=function(){
                        // call function to replace red with green
                        recolorImage(newImage,i, theChanged);

                    }
                    newImage.src= myImages[i].src;
    }

    function recolorImage(img_to_change, number, img_changed){

        var newCanvas = document.createElement('canvas');
        var ctx = newCanvas.getContext("2d");
        var theWidth = img_to_change.width;
        var theHeight = img_to_change.height;

        newCanvas.width = theWidth;
        newCanvas.height = theHeight;

        // draw the image on the temporary canvas
        ctx.drawImage(img_to_change, 0, 0, theWidth, theHeight);

        // pull the entire image into an array of pixel data
        var imageData = ctx.getImageData(0, 0, theWidth, theHeight);

        // CHANGE OF THE COLOR PIXELS

         for (var y = 0; y < theHeight; y ++) {
           for (var x = 0; x < theWidth; x ++) {
                 offset = ((y*(theWidth*4)) + (x*4));

                 imageData.data[offset + 0] =  Math.round((imageData.data[offset + 0] +imageData.data[offset + 1] +imageData.data[offset + 2] )/3);
                 imageData.data[offset + 1] = imageData.data[offset + 0];
                 imageData.data[offset + 2] = imageData.data[offset + 0];
           } //for
         } //for
         // final of the CHANGE OF THE PIXEL COLOR

        //------------------------------------------------------------------------------------------------------------------
        // put the altered data back on the canvas  
        ctx.putImageData(imageData,0,0);

        // put the re-colored image back on the image
        var finalImag = img_changed[number-1];
        finalImag.src = newCanvas.toDataURL();
    }

}); // end $(function(){});
</script>

<body>
    <p>First Image</p>
    <img src="imagens_250x180/rosa.jpg" name="image0" width=250 height=180 ><br/>
    <p>Second Image</p>
    <img src="imagens_250x180/ricardina.jpg" name="image1" width=250 height=180 >
     <p>Third Image</p>
    <img src="imagens_250x180/manaus.jpg" name="image2" width=250 height=180 >
</body>
</html>
user3059287
  • 56
  • 2
  • 10
  • Couldn't you use use css, kind of like http://net.tutsplus.com/tutorials/html-css-techniques/say-hello-to-css3-filters/ – Musa Dec 02 '13 at 23:57
  • See http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – Musa Dec 02 '13 at 23:59
  • I think the main problem here is that you're referencing the `i` inside your `newImage.onload`-function, which will do it's work asynchronously. When the onload is executed, the for-loop is probably done and the `i` is set to the latest index (`theImages.length - 1`). See my answer for an alternative solution. – bvx89 Dec 03 '13 at 00:36

2 Answers2

2

I didn't quite understand everything your code did, so I rewrote it quite a bit in order for me to understand it and make it work. You can probably take this as a starting point and rewrite back in to something that's more understandable for you:

$(function(){
  var theImages = document.getElementsByTagName("img");

  for (var i=0; i <theImages.length; i++){
    var newImage=new Image();

    /*
    * Store the current index as the new image's id
    * since the onload function is async
    */
    newImage.id = i;        
    newImage.onload=function(){

      // Retrieve the correct index
      var i = this.id;

      // Get width and height
      var theWidth = theImages[i].width;
      var theHeight = theImages[i].height;

      // create a temporary canvas to put the original image
      var tempCanvas=document.createElement("canvas");
      var tempCtx=tempCanvas.getContext("2d");

      // Set width and height of the canvas
      tempCanvas.width = theWidth;
      tempCanvas.height = theHeight;

      // Draw the original in a temporary canvas
      tempCtx.drawImage(this, 0, 0, theWidth, theHeight);

      // pull the entire image into an array of pixel data
      var imageData = tempCtx.getImageData(0, 0, theWidth, theHeight);

      // CHANGE OF THE COLOR PIXELS
      for (var y = 0; y < theHeight; y ++) {
        for (var x = 0; x < theWidth; x ++) {
       offset = ((y*(theWidth*4)) + (x*4));
         imageData.data[offset + 0] =  Math.round((imageData.data[offset + 0] +imageData.data[offset + 1] +imageData.data[offset + 2] )/3);
         imageData.data[offset + 1] = imageData.data[offset + 0];
         imageData.data[offset + 2] = imageData.data[offset + 0];
        } //for
      } //for

      // put the altered data back on the canvas  
      tempCtx.putImageData(imageData,0,0);

      // Set the original image
      theImages[i].src = tempCanvas.toDataURL();

    }
    newImage.src = theImages[i].src;    

  }
}); // end $(function(){});
bvx89
  • 868
  • 5
  • 12
  • Thank you, thank you, thank you. I tryed before to solve the problem by using some code similar to this. But it was not working because I am not confortable with the function toDataURL(). Then I tryed to adopt another code to my case. But, it was, obviously too obscure. Now, it is clear and simple. Once again: thank you very much. – user3059287 Dec 03 '13 at 10:36
  • You are very welcome. You could improve the code by putting the anonymous `onload`-function outside of the for-loop as shown [here](http://stackoverflow.com/questions/10320343/dont-make-functions-within-a-loop). – bvx89 Dec 03 '13 at 18:50
  • My program works very well on Explorer, but not in Chrome. I have the javscripts enabled in the Chrome settings. – user3059287 Feb 07 '14 at 17:35
  • Anyone have idea why it is happening ? – user3059287 Feb 07 '14 at 17:39
  • 1
    No, not really. It worked just fine on my setup, and I use Chrome as well. Perhaps you could do console.log() to find out why it's not working. – bvx89 Feb 13 '14 at 10:28
  • I did read some things about the issue. It seems I am not the first having the same troubles. I found out the some issues are about the code inside the css files. There, exists more than selectors, which brings problems when we are extracting the information about the styles associated to the selectors. I made some protections, which did help. However, it is not working properly yet. I must known why. So, now, I am reading some documents about "how to debug", by using the Chorme Dev Tools. I need to dive onto the running code to see what is happening. Once again, thank you. :-) – user3059287 Feb 14 '14 at 11:42
  • Well, it might be because this is very "risky" code. As you can see, the onload function is set first and then the temporary newImage.src is set to theImages[i].src. Inside the onload function, the newImage[i].src is set again. I don't know if this will call the onload function again or not, as I didn't properly test it. Anyway, wouldn't something [like this](http://demosthenes.info/blog/532/Convert-Images-To-Black-And-White-With-CSS) be easier – bvx89 Feb 17 '14 at 21:59
0

Guessing it might be caused by this typo: myImages[i] = theImages [i];. There shouldn't be a space after theImages.

SpliFF
  • 38,186
  • 16
  • 91
  • 120