Clearing ctx.fillStyle after drawing a filled rectangle doesn't work...
I've tried:
ctx.globalCompositeOperation = 'destination-out';
ctx.clearRect(0, 0, canvas.width, canvas.height); Clearing the canvas itself
ctx.fillStyle = "rgba(255, 255, 255, 1)"; Setting it to white
ctx.fillStyle = null; Maybe nulling it will work...
But I got no result other than reDrawing its previous value and not the new one.
JsFiddle: https://jsfiddle.net/q9ub0r9z/1/
Use ctx.fillRect() instead, see updated fiddle: https://jsfiddle.net/q9ub0r9z/2/
You use .fillRect function instead of .rect
function showRed(){
ctx.fillStyle = "rgba(255,0,0,1)";
ctx.fillRect(0,0, 200, 200);
}
function showGreen(){
ctx.fillStyle = "rgba(0,255,0,1)";
ctx.fillRect(200,0, 200, 200);
}
function showBlue(){
ctx.fillStyle = "rgba(0,0,255,1)";
ctx.rect(200,200, 200, 200);
}
function showBlack(){
ctx.fillStyle = "rgba(0,0,0,1)";
ctx.rect(200,200, 400, 400);
}
Related
I'm trying to draw a transparent circle on the HTML Canvas. Here is the code:
const canvas = document.querySelector('#canvas');
let ctx = canvas.getContext('2d');
canvas.width = 700;
canvas.height = 700;
ctx.beginPath();
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'destination-out';
ctx.clearRect(50, 50, 200, 200);
ctx.beginPath();
ctx.fillRect(50, 50, 200, 200);
ctx.moveTo(350, 150);
ctx.beginPath();
ctx.arc(350, 150, 80, 0, 2 * Math.PI, false);
ctx.closePath();
ctx.fill();
I was able to make the rectangle transparent with globalCompositeOperation set to destination-out but it failed to make the circle fully transparent.
Here is what MDN says about destination-out operation,
The existing content is kept where it doesn't overlap the new shape.
The circle has still some opaque color applied to it. How to make it fully transparent?
Here is the live demo.
Unable to understand what's wrong with the code. Any help would be appreciated.
You are using beginpath methods but the directives you use aren't paths.
The fully transparent rectangle is created by the clearRect and not by the fillRect method.
So you should set fillStyle to white and then the code works.
const canvas = document.querySelector('#canvas');
let ctx = canvas.getContext('2d');
canvas.width = 600;
canvas.height = 400;
ctx.fillStyle = 'rgba(0, 0, 0, .5)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
ctx.fillRect(25, 25, 150, 150);
ctx.arc(275, 100, 60, 0, 2 * Math.PI, false);
ctx.fill();
body {
background-color: skyblue;
position: relative;
}
img, #canvas {
position: absolute;
}
<body>
<img src="https://images.unsplash.com/photo-1635315850978-44cd0b237006?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1770&q=80" alt="" width=600 height=390>
<canvas id="canvas"></canvas>
</body>
I have these 4 layers.
What I'm trying to do is put the red and blue layer into one mask. But I don't want the purple or orange layer to be affected by this mask (only the red and blue). I manage to make it work for the orange but not for the purple layer
See my code
var canvas = document.querySelector('canvas');
canvas.height = window.innerHeight
canvas.width = window.innerWidth
var ctx = canvas.getContext('2d');
//this should'nt be affected by the mask
ctx.beginPath()
ctx.fillStyle = 'purple';
ctx.rect(0, 50, 100, 100);
ctx.fill()
//this is the mask
ctx.beginPath()
ctx.rect(10, 10, 70, 70);
ctx.fillStyle = 'green';
ctx.fill()
ctx.globalCompositeOperation = 'source-atop';
//this need to be inside the mask
ctx.beginPath()
ctx.fillStyle = 'blue';
ctx.rect(10, 10, 100, 100);
ctx.fill()
//this need to be inside the mask
ctx.beginPath()
ctx.fillStyle = 'red';
ctx.rect(50, 40, 100, 100);
ctx.fill()
ctx.globalCompositeOperation = 'source-over'; //reset
//this should'nt be affected by the mask
ctx.beginPath()
ctx.fillStyle = 'orange';
ctx.rect(200, 40, 100, 100);
ctx.fill()
And the fiddle https://jsfiddle.net/ws3b4q95/4/
Canvas doesn't know about shapes as objects, it only cares about pixels. So the purple rectangle can't be excluded from your mask, because everyting that's already drawn on the canvas, will be part of the mask.
Instead you should draw the rectangle after you've applied the mask, and use destination-over operation:
//this need to be inside the mask
ctx.beginPath()
ctx.fillStyle = 'red';
ctx.rect(50, 40, 100, 100);
ctx.fill()
//this should'nt be affected by the mask
ctx.globalCompositeOperation = 'destination-over';
ctx.beginPath()
ctx.fillStyle = 'purple';
ctx.rect(0, 40, 100, 100);
ctx.fill()
This is nice summary from Mozilla about composite operations: MDN web docs: CanvasRenderingContext2D.global .CompositeOperation
I've tried googling the answer to this but i'm just going around in circles....
If I clear the rect (using clearRect) then the image doesn't redraw after.
However, if I don't clear the images just stack.
What I want it to clear the current image and then draw with new one.
What am I missing?
Does it have something to do with the image Load ?
Sorry if this is a question repeat, I couldn't find the exact answer to this- and I tried what others suggested but results were poor.
http://jsfiddle.net/bxeuhh4h/
function clear() {
var canvasTemp = document.getElementById(imgSection);
var ctxTemp = canvasTemp.getContext("2d");
ctxTemp.clearRect(0, 0, 500, 500);
}
function fillColorOrPattern(imgSection,currentcolor){
if ((oldcolor !== currentcolor) || (oldxImgToBeFilled !== xImgToBeFilled)){
clear();
}
imgFill.onload = function () {
imgToBeFilled.onload = function () {
if ((oldcolor !== currentcolor) || (oldxImgToBeFilled !== xImgToBeFilled)){
fill(imgSection,currentcolor)
}
};
imgToBeFilled.src = xImgToBeFilled;
}
imgFill.src = xImgFill;
}
function fill(imgSection,currentcolor){
canvas = document.getElementById(imgSection);
ctx = canvas.getContext("2d");
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.globalCompositeOperation = "source-atop";
console.log(isItColorOrPattern);
if (isItColorOrPattern == "color"){
ctx.rect(0, 0, canvas.width, canvas.height);
console.log("currentcolor: " + currentcolor);
ctx.fillStyle = getColor(currentcolor);
console.log(getColor(currentcolor));
ctx.fill();
}else{
var pattern = ctx.createPattern(imgFill, 'repeat');
console.log("canvas.width: " + canvas.width);
console.log("xImgFill: " + xImgFill);
console.log(canvas.getContext);
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = pattern;
ctx.fill();
}
ctx.globalAlpha = .10;
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.drawImage(imgToBeFilled, 0, 0);
oldcolor = currentcolor;
oldxImgToBeFilled = xImgToBeFilled;
}
$(window).load(function(){
imgToBeFilled = new Image();
imgFill = new Image();
fillColorOrPattern(imgSection,currentcolor);
}
You need to add a beginPath() in there. rect() will accumulate rectangles to the path, clearRect() won't clear those. Also reset comp. mode and alpha as they are sticky.
You could avoid beginPath() if you use fillRect() instead of rect() + fill() (added example below) as fillRect() does not add to the path.
function fill(imgSection,currentcolor){
// these should really be initialized outside the loop
canvas = document.getElementById(imgSection);
ctx = canvas.getContext("2d");
// clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// clear path
ctx.beginPath();
// use default comp. mode
ctx.globalCompositeOperation = "source-over";
// reset alpha
ctx.globalAlpha = 1;
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.globalCompositeOperation = "source-atop";
if (isItColorOrPattern === "color"){
// rect() accumulates on path
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = getColor(currentcolor);
ctx.fill();
// instead of rect() + fill() you could have used:
// fillRect() does not accumulate on path
// fillRect(0, 0, canvas.width, canvas.height);
}
else {
var pattern = ctx.createPattern(imgFill, 'repeat');
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = pattern;
ctx.fill();
}
ctx.globalAlpha = .1;
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.drawImage(imgToBeFilled, 0, 0);
oldcolor = currentcolor;
oldxImgToBeFilled = xImgToBeFilled;
}
Canvas workflow goes like this:
Draw some things on the canvas.
Calculate changes to the position of those things.
Clear the canvas.
Redraw all the things in their new positions.
Canvas does not "remember" where it drew your things so you cannot directly order your things to move.
But you can save the definition of your things in javascript object:
var myCircle={
centerX:50,
centerY:50,
radius:25,
fill:'blue'
}
Then you can "move" your things using the javascript objects:
myCircle.centerX += 5;
And then redraw the things at their new positions. Putting the redraw code in a function makes redrawing easier:
function redraw(){
// clear the canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// redraw one or more things based on their javascript objects
ctx.beginPath();
ctx.arc( myCircle.centerX, myCircle.centerY, myCircle.radius, 0, Math.PI*2 );
ctx.closePath();
ctx.fillStyle=myCircle.fill;
ctx.fill();
}
Putting it all together:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var myCircle={
centerX:50,
centerY:50,
radius:25,
fill:'blue'
}
redraw();
document.getElementById('move').addEventListener('click',function(){
myCircle.centerX+=5;
redraw();
});
function redraw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.arc( myCircle.centerX, myCircle.centerY, myCircle.radius, 0, Math.PI*2 );
ctx.closePath();
ctx.fillStyle=myCircle.fill;
ctx.fill();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<button id=move>Move</button>
<br>
<canvas id="canvas" width=300 height=300></canvas>
I'm making a game with javascript canvas. I'm drawing all the game elements, like the player, blocks and lines, but I don't want you to see all that. Instead want the whole screen to be black, expect for in some places where there is lightsources. I don't need any shadows, just a circle on the screen that is lit up with a radial gradient. I am able to achieve this for one lightsource by adding a transparent gradient after I have drawn everything else, like this: (imagine the red rectangle to be all the things in the game)
//Drawing all the game elements
ctx.fillStyle = "red";
ctx.fillRect(100, 100, 400, 300);
//adding the darkness and the lightsources
var grd = ctx.createRadialGradient(150, 100, 5, 150, 100, 100);
grd.addColorStop(0, "transparent");
grd.addColorStop(1, "black");
ctx.fillStyle = grd; ctx.fillRect(0, 0, 600, 400);
JSFiddle
But how can I achieve this with multiple lightsources? The technique showed won't work.
I have tried using the Illuminated.js api, but that was incredibly slow, and I don't need anything of the shadows and all that fancy stuff, just a circle where you can see the game.
Here's an example of my approach - create black&white mask and multiply the base with it:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
//Drawing all the game elements
ctx.fillStyle = "red";
ctx.fillRect(100, 100, 400, 300);
//adding the darkness and the lightsources
function addlight(ctx, x, y) {
var grd = ctx.createRadialGradient(x, y, 10, x, y, 150);
grd.addColorStop(0, "white");
grd.addColorStop(1, "transparent");
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 600, 400);
}
var buffer = document.createElement('canvas');
buffer.width = 600;
buffer.height = 400;
b_ctx = buffer.getContext('2d');
b_ctx.fillStyle = "black";
b_ctx.fillRect(0, 0, 600, 400);
addlight(b_ctx, 150, 100);
addlight(b_ctx, 350, 200);
addlight(b_ctx, 450, 250);
ctx.globalCompositeOperation = "multiply";
ctx.drawImage(buffer, 0, 0);
<canvas id="myCanvas" width="600" height="400" style="border:1px solid #d3d3d3;">
How can you fill a shape created in javascript with an image?
I am using a shape I created with javascript and am right now filling it with a color. How can I replace that and fill it with an image/gif of my choice?
function shape(x,y) {
ctx.fillStyle = "#9dd4ff";
ctx.beginPath();
ctx.moveTo(232,213)
ctx.lineTo(315,198);
ctx.lineTo(x,y);
ctx.closePath();
ctx.fill();
}
You can solve this problem by using your path as a MASK
HERE IS THE CODE
// Create an image element
var img = document.createElement('IMG');
// When the image is loaded, draw it
img.onload = function () {
// Save the state, so we can undo the clipping
ctx.save();
// Create a shape, of some sort
ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(100, 30);
ctx.lineTo(180, 10);
ctx.lineTo(200, 60);
ctx.arcTo(180, 70, 120, 0, 10);
ctx.lineTo(200, 180);
ctx.lineTo(100, 150);
ctx.lineTo(70, 180);
ctx.lineTo(20, 130);
ctx.lineTo(50, 70);
ctx.closePath();
// Clip to the current path
ctx.clip();
ctx.drawImage(img, 0, 0);
// Undo the clipping
ctx.restore();
}
// Specify the src to load the image
img.src = "https://www.google.com/images/srpr/logo3w.png";
Try this:
var thumbImg = document.createElement('img');
thumbImg.src = 'path_to_image';
function shape(x,y) {
ctx.fillStyle = "#9dd4ff";
ctx.beginPath();
ctx.moveTo(232,213)
ctx.lineTo(315,198);
ctx.lineTo(x,y);
ctx.drawImage(thumbImg, 0, 0, 50, 50);
ctx.closePath();
ctx.fill();
}
-- Edit --
Remove ctx.fillStyle ="#9dd4ff"
As you need to mention the path of image.