Skip to content
Advertisement

I want to change the color of an image inside a image in html5-canvas

I’m using canvas to make a badge. To draw the image I used the code :

let image = new Image()
image.src = 'imageSource'
image.onload = () => {
 ctx.drawImage(image, xOffset, yOffset, newWidth, newHeight)

 // to color the image at the back (works properly)

 ctx.globalCompositeOperation = 'source-in'
 ctx.fillStyle = 'someColour'
 ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)
 ctx.globalCompositeOperation = 'source-over'

  // icon in the middle

  let iconImage = new Image()
  iconImage.src = 'iconSource'
  iconImage.onload = () => {
      ctx.drawImage(iconImage, xOffset, yOffset, width, height)

      // i need to be able to fill color in this iconImage only
}
  

The preview is like this.

canvas-image

Now, to color the images, I’ve tried using different blend modes. It works fine when I work it around for the background that is image. I tried to do it for the iconImage the same way, but it didn’t work. I want to color the icon in the middle without changing any other thing.

Advertisement

Answer

I was bored in the morning and makes this example for you in this example you can see all the elements in the canvas can be modified.

Note: Because of the CORS issue(Tainted canvases may not be exported), can’t edit the color of the external image here, so import your image using Choose File then change the image color!

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d")
const inputs = document.querySelectorAll("input");
const xOffset = 30, yOffset = 10, width = canvas.width-60, height = canvas.height-20;

var inputValues = {stroke:"#8db5c2",fill:"white",text:"Text",image:"https://i.stack.imgur.com/8eLMW.png",imageColor:"grey"}

inputs.forEach(input => {
  input.addEventListener("input", function() {
    if(this.id === "image") {
      if (!input.files || !input.files[0]) return;
      const FR = new FileReader();
      FR.onloadend = (evt) => {
          inputValues = {...inputValues,[this.id]:FR.result};
          DrawBadge(inputValues)
      };
      FR.readAsDataURL(input.files[0]);
    } else {
      inputValues = {...inputValues,[this.id]:this.value};
      DrawBadge(inputValues)
    }
  })
})

DrawBadge(inputValues)

function DrawBadge ({stroke, fill, text, image ,imageColor}) {
  //Draw Badge
  ctx.strokeStyle = stroke;
  ctx.lineWidth = 15;
  ctx.fillStyle = fill;
  roundRect(ctx, xOffset, yOffset, width, height, {
    tl: 1,
    tr: 1,
    bl: width/2,
    br: width/2,
  });
  //Draw Text
  ctx.font = "20px Arial";
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = "black";
  ctx.fillText(text,width/2+xOffset,height*0.8);
  //Draw Image
  const firstImage = new Image();
  const insideWidth = 80, insideHeight = 80;
  firstImage.src = image;
  // Because of the CORS issue just show image as it is
  if(image === "https://i.stack.imgur.com/8eLMW.png") {
    firstImage.onload = () => {
      ctx.drawImage(firstImage, (width/2)-(insideWidth/2)+xOffset,height*0.2,insideWidth , insideHeight);
     }
  // you should use this function for changing image color
  } else {
    firstImage.onload = () => {
      //Make new canvas for image
      const imageCtx = document.createElement("canvas").getContext("2d");
      const insideImage = new Image();
      imageCtx.canvas.width = insideWidth;
      imageCtx.canvas.height = insideHeight;
      imageCtx.save();
      imageCtx.fillStyle = imageColor;
      imageCtx.fillRect(0, 0, insideWidth, insideHeight);
      //Here magic happend 
      imageCtx.globalCompositeOperation = "destination-in";
      imageCtx.drawImage(firstImage,0,0,insideWidth,insideHeight);
      //Then export our canvas to png image
      insideImage.src = imageCtx.canvas.toDataURL("image/png");
      insideImage.onload = () => {
          ctx.drawImage(insideImage,(width/2)-(insideWidth/2)+xOffset,height*0.2,insideWidth,insideHeight);
      }
    }
  }
}

function roundRect(ctx, x, y, width, height, radius, fill, stroke){
  ctx.beginPath();
  ctx.moveTo(x + radius.tl, y);
  ctx.lineTo(x + width - radius.tr, y);
  ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
  ctx.lineTo(x + width, y + height - radius.br);
  ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
  ctx.lineTo(x + radius.bl, y + height);
  ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
  ctx.lineTo(x, y + radius.tl);
  ctx.quadraticCurveTo(x, y, x + radius.tl, y);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
}
body {
 display: flex;
}
#inputs {
  display: flex;
  flex-direction: column;
}
canvas {
   border: 1px solid;
}
<body>
  <div id="inputs">
    Stroke Color: <input id="stroke" type="color" value="#8db5c2">
    Fill Color: <input id="fill" type="color" value="#ffffff">
    Text: <input id="text" type="text" value="Text">
    <lable>
    Image:<input id="image" type="file"accept="image/png, image/jpeg">
     ImageColor: <input id="imageColor" type="color" value="#808080">
     </lable>
  </div>
  <canvas width="220" height="190"></canvas>
</body>
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement