Related
I can't see that this had been posted already, so here goes.
Let's say i draw 2 squares on the canvas.
var c = document.getElementById('test'), ctx = c.getContext('2d');
ctx.fillStyle = "rgba(0,0,255,0.5)";
ctx.beginPath();
ctx.moveTo(25, 0);
ctx.lineTo(50, 50);
ctx.lineTo(0, 50);
ctx.lineTo(25, 0);
ctx.fill();
ctx.fillStyle = "rgba(255,0,0,0.5)";
ctx.beginPath();
ctx.moveTo(50, 0);
ctx.lineTo(75, 50);
ctx.lineTo(25, 50);
ctx.lineTo(50, 0);
ctx.fill();
This produces this image:
If i change globalAlpha to 0.5, i get this:
However, i want to produce this:
As in, all pixels are transparent and any images under it will appear, but the pixels created by the red triangle will overwrite the existing blue triangle where it is drawn.
And ctx.globalComposisteOperation doesn't seem to help in this instance due to it also factoring the transparency and the fact i want to keep both squares.
Is there any way to do this with current methods?
Use Compositing to clear the red triangle before drawing it.
Using compositing is slightly better than clipping because you don't have to clear the clip. Clearing a clip requires saving the entire context state and then restoring that context state -- many properties involved. Compositing just requires changing 1 property forth and back.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// fill the blue rect
ctx.fillStyle = "rgba(0,0,255,0.5)";
ctx.beginPath();
ctx.moveTo(25, 0);
ctx.lineTo(50, 50);
ctx.lineTo(0, 50);
ctx.lineTo(25, 0);
ctx.fill();
// define the red rect path
ctx.beginPath();
ctx.moveTo(50, 0);
ctx.lineTo(75, 50);
ctx.lineTo(25, 50);
ctx.lineTo(50, 0);
// clear the red rect path using compositing
ctx.globalCompositeOperation='destination-out';
ctx.fillStyle='black';
ctx.fill();
ctx.globalCompositeOperation='source-over';
// fill the red rect
ctx.fillStyle = "rgba(255,0,0,0.5)";
ctx.fill();
body{ background-color:white; }
#canvas{border:1px solid red; }
<canvas id="canvas" width=512 height=512></canvas>
Layers
Do it the photoshop way and create layers to do the work for you. Creating a layer (second canvas) is no more trouble than loading an image. You can create dozens and have no problem and it makes this type of work easy.
First create a second canvas (layer)
// canvas is original canvas
var layer = document.createElement("canvas");
layer.width = canvas.width; // same size as original
layer.height = canvas.height;
var ctx1 = layer.getContext("2d");
Then draw your triangles on the second canvas with alpha = 1;
var tri = (x,c)=>{
ctx1.fillStyle = c;
ctx1.beginPath();
ctx1.moveTo(25 + x, 0);
ctx1.lineTo(50 + x, 50);
ctx1.lineTo(0 + x, 50);
ctx1.closePath();
ctx1.fill();
}
tri(0,"#00f");
tri(25,"#f00");
Then just draw that layer on top of the canvas you are working on with the alpha value you want.
ctx.globalAlpha = 0.5;
ctx.drawImage(layer,0,0);
If you don't need the extra layer delete the canvas and context by dereferencing them.
ctx1 = undefined;
layer = undefined;
Or you can keep the layer , and make another layer for the background and mix them in real time to get the FX just right
//
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
var layer = document.createElement("canvas");
layer.width = canvas.width; // same size as original
layer.height = canvas.height;
var ctx1 = layer.getContext("2d");
var tri = (x,c)=>{
ctx1.fillStyle = c;
ctx1.beginPath();
ctx1.moveTo(25 + x, 0);
ctx1.lineTo(50 + x, 50);
ctx1.lineTo(0 + x, 50);
ctx1.fill();
}
tri(0,"#00f");
tri(25,"#0f0");
tri(50,"#f00");
ctx.globalAlpha = 0.5;
ctx.drawImage(layer,0,0);
layer = ctx1 = undefined;
You can clear the area for the second rectangle before drawing it.
var c = document.getElementById('game'),
ctx = c.getContext('2d');
ctx.globalAlpha = 0.5;
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, 50, 50);
// Clear the extra bit of the blue rectangle
ctx.clearRect(25, 25, 25, 25);
ctx.fillStyle = "red";
ctx.fillRect(25, 25, 50, 50);
<canvas id="game" width="320" height="240"></canvas>
I would honestly suggest just changing the colors and avoid using an alpha.
var c = document.getElementById('game'),
ctx = c.getContext('2d');
ctx.fillStyle = "#8080FF";
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = "#ff8080";
ctx.fillRect(25, 25, 50, 50);
<canvas id="game" width="320" height="240"></canvas>
var c = document.getElementById('test'), ctx = c.getContext('2d');
ctx.save();
ctx.fillStyle = "rgba(0,0,255,0.5)";
ctx.beginPath();
ctx.moveTo(25, 0);
ctx.lineTo(50, 50);
ctx.lineTo(0, 50);
ctx.lineTo(25, 0);
ctx.fill();
ctx.fillStyle = "rgba(255,0,0,0.5)";
ctx.beginPath();
ctx.moveTo(50, 0);
ctx.lineTo(75, 50);
ctx.lineTo(25, 50);
ctx.lineTo(50, 0);
ctx.clip();
ctx.clearRect(0, 0, 100, 100);
ctx.fill();
ctx.restore();
Just use clip() to take the current path, clear everything in there with clearRect, then draw the path as normal.
#markE is right, use compositing instead of clipping (really heavy).
However, his solution will only work if you've drawn everything with an rgba color. Maybe I read your question wrongly, but if you're going from an opaque shape and want to make it transparent, then you should rather use the copy gCO and the globalAlpha property.
This will have less performance impact since drawImage is faster than fill, and will allow you to perform a fade-out effect ; but it really depends on your needs.
var ctx = c.getContext('2d');
// initial blue
ctx.fillStyle = "#0000FF";
ctx.beginPath();
ctx.moveTo(25, 0);
ctx.lineTo(50, 50);
ctx.lineTo(0, 50);
ctx.lineTo(25, 0);
ctx.fill();
setTimeout(function drawRed() {
ctx.fillStyle = "#FF0000";
ctx.beginPath();
ctx.moveTo(50, 0);
ctx.lineTo(75, 50);
ctx.lineTo(25, 50);
ctx.lineTo(50, 0);
ctx.fill();
}, 500);
btn.onclick = function makeItTransparent() {
// if we weere to make this into an animation, we would set it only once
ctx.globalCompositeOperation = 'copy';
ctx.globalAlpha = .8;
// draw the canvas on itself
ctx.drawImage(c, 0, 0);
// once again, in an animation we won't reset this to default
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = 'source-over';
};
/* checkboard background */
canvas {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWNgYGBowIKxgqGgcJA5h3yFAOI3GQFqqi5ZAAAAAElFTkSuQmCC");
}
<canvas id="c"></canvas>
<button id="btn">make it transparent</button>
I cannot seem to get a second clip call working in canvas. See fiddle: http://jsfiddle.net/m2hL17nu/
Notice how the first radial grad is clipped but the second isn't.
I have seen Can you have multiple clipping regions in an HTML Canvas? but save restore still doesn't seem to be letting the next clip() work.
Thanks in advance for your help. See code below:
var x1 = 300,
y1 = 100,
x2 = 50,
y2 = 50,
r = 20;
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
function createRadialGradient (xa, ya, xb, yb, r) {
var grd = context.createRadialGradient(xa, ya, 0, xb, yb, r);
grd.addColorStop(0, 'rgba(0,0,0,1)');
grd.addColorStop(1, 'rgba(0,0,0,0)');
context.fillStyle = grd;
context.fill();
}
context.save();
context.rect(x1-r,y1-r,r,r);
context.clip();
context.rect(0, 0, canvas.width, canvas.height);
createRadialGradient(x1, y1, x1, y1, r);
context.restore();
context.save();
context.rect(x2-r,y2,r,r);
context.strokeStyle = 'black';
context.clip();
context.rect(0, 0, canvas.width, canvas.height);
createRadialGradient(x2, y2, x2, y2, r);
context.stroke();
you should use beginPath() and closePath() before drawing the clips and after the clip() methods respectively:
var x1 = 300,
y1 = 100,
x2 = 50,
y2 = 50,
r = 20;
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
function createRadialGradient (xa, ya, xb, yb, r) {
var grd = context.createRadialGradient(xa, ya, 0, xb, yb, r);
grd.addColorStop(0, 'rgba(0,0,0,1)');
grd.addColorStop(1, 'rgba(0,0,0,0)');
context.fillStyle = grd;
context.fill();
}
context.save();
context.beginPath();
context.rect(x1-r,y1-r,r,r);
context.closePath();
context.clip();
context.rect(0, 0, canvas.width, canvas.height);
createRadialGradient(x1, y1, x1, y1, r);
context.restore();
context.save();
context.beginPath();
context.rect(x2-r,y2,r,r);
context.closePath();
context.clip();
context.strokeStyle = 'black';
context.rect(0, 0, canvas.width, canvas.height);
createRadialGradient(x2, y2, x2, y2, r);
context.stroke();
<canvas id="myCanvas" width="500" height="500"></canvas>
The save() and restore() does not affect the content of the path object itself - only the state of the context (which includes clipping state).
restore() can remove the clip definition but if the path still contains data it will be activated next time you call clip regardless, together with new data added to the path.
To make it work you need to make sure you're working with a clean path. For that simply call beginPath() before defining the clip area with rect(). closePath() is really not necessary with clip() as clip() (and fill()) will close implicit due to it being impossible to clip with an open path.
Then before doing actual drawing call beginPath() again as clipping is now a part of the context state. Finally use restore() to remove the clipping definition (revert state to previous).
context.save(); // store current state to stack
context.beginPath(); // clean path
context.rect(x1-r,y1-r,r,r);
context.clip(); // path is closed; clip is now activated
context.beginPath(); // clean path for your new shape
context.rect(0, 0, canvas.width, canvas.height);
createRadialGradient(x1, y1, x1, y1, r);
...
context.restore(); // removes the clip from state
var ctx = canvas.getContext('2d');
ctx.save();
ctx.beginPath();
ctx.rect(50, 50, 50, 50);
ctx.clip();
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#900';
ctx.fill();
ctx.restore();
ctx.save();
ctx.beginPath();
ctx.rect(150, 80, 80, 60);
ctx.clip();
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#009';
ctx.fill();
ctx.restore();
<canvas id=canvas width=500 height=180></canvas>
I want to add an image on the canvas and then zoom/Scale it
I am trying to add this image file but unable to do so though I can add square and then zoom the canvas.
https://dl.dropboxusercontent.com/u/139992952/houseIcon.png
how can I add the image.
Fiddle:
http://jsfiddle.net/ndYdk/11/
function draw(scale, translatePos){
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
// clear canvas
context.clearRect(0, 0, canvas.width, canvas.height);
context.save();
context.translate(translatePos.x, translatePos.y);
context.scale(scale, scale);
context.beginPath(); // begin custom shape
context.rect(20, 20, 50, 50);
context.fillStyle = 'red';
context.fill();
context.lineWidth = 2;
context.strokeStyle = 'black';
context.stroke();
context.closePath(); // complete custom shape
var grd = context.createLinearGradient(-59, -100, 81, 100);
context.fillStyle = grd;
context.fill();
context.stroke();
context.restore();
}
Any help will be really appreciated.
On button click I just want to put element in the canvas, but it's not working. Here is the code I have written:
<script language="javascript" type="text/javascript">
function image()
{
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.clear();
ctx.setTransform(-1, 0, 0, 1, 200, 200);
context.clear(true)
var imgobj=new Image();
imgobj.onload=function()
{
ctx.drawImage(imgobj,69,50);
}
imgobj.src='images.jpg';
}
function circle()
{
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.clear();
// do some more drawing
ctx.setTransform(-1, 0, 0, 1, 200, 200);
// do some drawing with the new transform
context.clear(true);
ctx.beginPath();
ctx.arc(95,50,40,0,2*Math.PI);
ctx.stroke();
}
function line()
{
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.clear();
// do some more drawing
ctx.setTransform(-1, 0, 0, 1, 200, 200);
// do some drawing with the new transform
context.clear(true);
ctx.moveTo(0,0);
ctx.lineTo(200,100);
ctx.stroke();
</script>
</head>
<body>
<h1>Demo of HTM5</h1>
<div class="canvas1" align="left">
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #000000;">
</canvas>
<p><button onclick="circle()"> Draw Circle</button></p>
<p><button onclick="line()"> Draw Line</button></p>
<p><button onclick="image()"> Display Image</button></p>
</div>
</body>
</html>
There is no ctx.clear() method on the context object, try instead with:
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
You are also using an unassigned context which you should remove.
Also there is no need to look-up canvas inside the function each time, just do it once in global scope.
And also, in your line method you're missing a beginPath() call.
See fiddle here
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
function image() {
/// use this to clear canvas
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.setTransform(-1, 0, 0, 1, 200, 200);
var imgobj = new Image();
imgobj.onload = function () {
ctx.drawImage(imgobj, 69, 50);
}
imgobj.src = 'images.jpg';
}
function circle() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// do some more drawing
ctx.setTransform(-1, 0, 0, 1, 200, 200);
// do some drawing with the new transform
ctx.beginPath();
ctx.arc(95, 50, 40, 0, 2 * Math.PI);
ctx.stroke();
}
function line() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// do some more drawing
ctx.setTransform(-1, 0, 0, 1, 200, 200);
// do some drawing with the new transform
ctx.beginPath(); /// add a beginPath here
ctx.moveTo(0, 0);
ctx.lineTo(200, 100);
ctx.stroke();
}
I think, you are using some wrong approach to clear the canvas. There is no method clear for canvas to clear it.
instead you should use: .clearRect(0, 0, 300, 300);
Here is the sample code:
function circle(){
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.clearRect(0, 0, 300, 300);
// do some more drawing
ctx.setTransform(-1, 0, 0, 1, 200, 200);
ctx.beginPath();
ctx.arc(95,50,40,0,2*Math.PI);
ctx.stroke();
}
function line(){
var c = document.getElementById('myCanvas');
var ctx = c.getContext('2d');
ctx.clearRect(0, 0, 300, 300);
ctx.setTransform(-1, 0, 0, 1, 200, 200);
// do some drawing with the new transform
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(200,100);
ctx.stroke();
}
jsfiddle: http://jsfiddle.net/FumBm/3/
Also, there is no variable with name context in your script. :)
Is it possible to create a path in canvas of which the lines are stroked differently? For example so you could draw a box being green on the inside, with 4 different colored edges. I've included some code, but that does not work, because it draws the path twice (first time unfinished path, second time the whole path).
JavaScript
window.onload = function()
{
canvas = document.getElementById("canvas1");
if (canvas.getContext)
{
ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.save();
ctx.moveTo(0, 0);
ctx.strokeStyle = "rgb(255, 0, 0)";
ctx.lineTo(100, 100);
ctx.stroke();
ctx.strokeStyle = "rgb(0, 0, 255)";
ctx.lineTo(200, 100);
ctx.stroke();
ctx.restore();
}
}
HTML
<canvas id = "canvas1" width = "400" height = "400">
Your browser does not support the HTML5 canvas tag.
</canvas>
I think you'll have to use two paths.
ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(100, 100);
ctx.strokeStyle = "rgb(255, 0, 0)";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.strokeStyle = "rgb(0, 0, 255)";
ctx.stroke();