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.
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>