HTML5 - Canvas Shape Stroke - html

I have created 2 shapes, circle and rectangle, one on top of the other to resemble a key lock. I then try to apply a stroke but its stroking both shapes. What I want it to do is just stroke the merged pattern and not any of the intersections.
context.beginPath();
context.fillStyle = "#ccc";
context.arc(115, 550, 12, 0, 2 * Math.PI, false);
context.moveTo(105, 555);
context.fillStyle = "#999";
context.rect(105, 555, 20, 30);
context.fill();
context.stroke();
context.closePath();
If I try to draw the rect first, then the arc on top there are extra line paths when you stroke, its like I have to close Path and then draw it again.

You can't use a rect and a whole circle if you want the path to do without the intersecting part.
Instead you have to draw only part of the circle and only part of the rectangle. This should do it for you:
context.beginPath();
context.fillStyle = "#ccc";
context.arc(115, 550, 12, 2.5, 2.2 * Math.PI, false);
context.moveTo(105+20, 555);
context.fillStyle = "#999";
// instead of a rect, we really want three lines
context.lineTo(105+20,555+30);
context.lineTo(105,555+30);
context.lineTo(105,555);
context.fill();
context.stroke();
context.closePath();

While working on my own irregular shape answer, I discovered a lab project by Professor Cloud that solved my problem.
This page, SVG-to-Canvas, parses an SVG graphic to Canvas code. So if you have an application like Illustrator with which you can draw and save the graphic as SVG, then you can parse the usable canvas codes and just plug them in.

You can use compositing and temporary canvas. Something like that:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var tempCanvas = document.getElementById('tempCanvas');
var tempContext = tempCanvas.getContext('2d');
tempContext.save();
// clear temp context
tempContext.clearRect(0, 0, canvas.width, canvas.height);
// draw all rects with strokes
tempContext.beginPath();
tempContext.strokeStyle='red';
tempContext.lineWidth=3;
tempContext.arc(100, 100, 60, 0, 2 * Math.PI, false);
tempContext.rect(20,150,100,200);
tempContext.stroke();
// set compositing to erase existing drawings
// where the new drawings are drawn
tempContext.globalCompositeOperation='destination-out';
// fill all rects
// This "erases" all but the outline stroke
tempContext.beginPath();
tempContext.fillStyle='blue';
tempContext.arc(100, 100, 60, 0, 2 * Math.PI, false);
tempContext.rect(20,150,100,200);
tempContext.fill();
// draw outlines from tempcanvas into canvas
ctx.drawImage(tempCanvas, 0, 0);
// draw into canvas
ctx.beginPath();
ctx.fillStyle='green';
ctx.globalAlpha = 0.2;
ctx.rect(20,150,100,200);
ctx.arc(100, 100, 60, 0, 2 * Math.PI, false);
ctx.fill();
tempContext.restore();
And a jsfiddle: https://jsfiddle.net/EvaF/8to68dtd/2/

Related

Using globalCompositeOperation in few phases

I'm drawing many things on my context. Shapes, texts, images..
I want to use achieve the same effect i'm getting using the clip method on the context with globalCompositeOperation (Using the clip is harder for me to perform and i don't know if possible for texts)
The user can draw few shapes. and then start a mask phase. to draw some more shapes, texts.. which would draw into the mask and then the next draw will be clipped in the masked phase. and then continue to regular drawing...
For ex.
The user draw this drawing
Then started masked mode and drew this 2 red lines
Then he stopped drawing into the mask, and start drawing rectangle to consider the mask
And finally applied the mask clipping and the result should look like this
I've managed to clip the rectangle with the lines if there were no earlier drawings.
// Starting the mask phase
ctx.strokeStyle = 'red';
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(240, 140);
ctx.moveTo(80, 20);
ctx.lineTo(300, 140);
ctx.stroke();
ctx.globalCompositeOperation = 'source-out';
ctx.fillStyle = 'cyan';
ctx.fillRect(50, 70, 250, 20);
// Setting the composition back
ctx.globalCompositeOperation = 'source-over';
but when i'm adding my drawings in the beginning of the code, the composition considering it as well.
ctx.fillStyle = 'brown';
ctx.beginPath();
ctx.arc(80, 80, 50, 50, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'yellow';
ctx.fillRect(80, 60, 150, 40);
ctx.fillStyle = 'black';
ctx.font = '40pt arial';
ctx.fillText('Hello', 130, 110);
// How to tell the context to start from here the compisition ???
How to tell the context to start composition from a certain point, if possible ?
I could create another canvas and draw the mask there.. and then draw the new canvas on the main canvas. But there is better solution ?
You can change the compositing mode at any point in the drawing flow by changing .globalCompositeOperation. But, as you've discovered, any new compositing mode will also affect existing canvas content.
Your intuition is correct about using a second "staging canvas" to do compositing that won't destroy your existing content.
You can use an in-memory canvas to do compositing and create your rect-with-erased-lines. Then you can drawImage this in-memory canvas to your main canvas. Since the compositing was done on your in-memory canvas, your existing circle-hello content is not undesirably affected by compositing.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// create an in-memory canvas to do compositing
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/temp6a.png";
function start(){
ctx.drawImage(img,0,0);
addCompositedRectangle();
ctx.drawImage(c,0,0);
}
function addCompositedRectangle(){
// resize the in-memory canvas
// Note: this automatically clears any previous content
// and also resets any previous compositing modes
c.width=300; // largest width;
c.height=140; // largest height;
// Starting the mask phase
cctx.strokeStyle = 'red';
cctx.lineWidth = 5;
cctx.beginPath();
cctx.moveTo(20, 20);
cctx.lineTo(240, 140);
cctx.moveTo(80, 20);
cctx.lineTo(300, 140);
cctx.stroke();
cctx.globalCompositeOperation = 'source-out';
cctx.fillStyle = 'cyan';
cctx.fillRect(50, 70, 250, 20);
// Setting the composition back
cctx.globalCompositeOperation = 'source-over';
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

Context scaling breaks some drawing

I'm trying to draw on a virtual canvas of proportions 1x1 so that I don't have to constantly multiply out the actual dimensions.
This works perfectly when I draw circles, but it would appear as though it does not work well with rectangles, and I'm not sure why.
When I have the following code:
var canvas = this.canvas;
var ctx = canvas.getContext("2d");
ctx.scale(this.canvas.width, this.canvas.height);
ctx.beginPath();
ctx.arc(.5, .5, .1, 0, 2*Math.PI);
ctx.fillStyle = "red";
ctx.fill();
It works fine, and the circle scales with the canvas.
However, when I merely change it to the following:
var canvas = this.canvas;
var ctx = canvas.getContext("2d");
ctx.scale(this.canvas.width, this.canvas.height);
ctx.fillStyle = "blue";
ctx.fillRect(0,0, .0001, .001);
ctx.beginPath();
ctx.arc(.5, .5, .1, 0, 2*Math.PI);
ctx.fillStyle = "red";
ctx.fill();
the rectangle takes up the entirety of the screen and even covers up the circle, even though the circle is drawn after the rectangle. It should, obviously, be taking up a very minute amount of space on the canvas.
It might be worth noting that this occurs in a game loop.
However, the following works as expected, with a red circle appearing above a blue backdrop
var canvas = this.canvas;
var ctx = canvas.getContext("2d");
ctx.fillStyle = "blue";
ctx.fillRect(0,0, 50, 50);
ctx.beginPath();
ctx.arc(10, 10, 20, 0, 2*Math.PI);
ctx.fillStyle = "red";
ctx.fill();
If you're calling this repeatedly in a game loop, the scale method will increase the scale of your transform, every time through the loop. So you end up with everything growing. Since you're not showing your actual code, it's hard to tell why the circle isn't affected.
Try calling scale() only once, or use save() and restore(), or just reset the transform at the start of the loop, before calling scale():
ctx.setTransform(1, 0, 0, 1, 0, 0);

A smooth circle in Html5

I'm trying to draw a circle with a canvas in HTML5. I use an example from w3schools, but the result is ugly, not smooth. Is it possible to have a smooth circle ? (I tried this with Chrome and IE9)
The code I use :
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.arc(125,120,100,0,2*Math.PI);
ctx.stroke();
=> http://www.w3schools.com/html/tryit.asp?filename=tryhtml5_canvas_tut_path2 : the original example
=> http://jsfiddle.net/jPeKk/ : my try, bigger
On chrome, this is a known issue that has been discussed here - Can I turn off antialiasing on an HTML <canvas> element?
and here - How to anti-alias clip() edges in html5 canvas under Chrome Windows?
Try
ctx.webkitImageSmoothingEnabled=true;
to make your line smoother.
Another solution that works for me (though I think it's more of a hack than a proper solution) is to draw a line around the circle with the same color. For some reason this is going to be very smooth.
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.beginPath();
context.arc(100, 100, 60, 0, 2 * Math.PI, false);
context.fillStyle = '#775599';
context.fill();
context.beginPath();
context.arc(200, 200, 60, 0, 2 * Math.PI, false);
context.fillStyle = '#775599';
context.fill();
context.lineWidth = 2;
context.strokeStyle = '#775599';
context.stroke();

HTML5 Canvas context fill doesn't work on Chrome

I have created a circular pattern generator using HTML5 canvas. I have used the arc to create circles and have placed them randomly on the canvas. Also, I have the circles filled with a random color generated on mousedown. The arc fill works on Firefox but doesnt show on Chrome. I am using Latest Chrome ver 18.
context.save();
context.scale(1, 1);
context.beginPath();
context.arc(nextX, nextY, radius, 0, 2 * Math.PI, false);
context.closePath();
context.fillStyle = color;
context.lineWidth = 1;
context.strokeStyle = color;
context.stroke();
context.fill();
context.restore();
Here is the live working link - http://www.riaxe.com/html5cirpattern/

using action script how to draw a one circle with diffrent color's

using action script how to draw a circle that have more than 2 or 3 color'si.e if circle is divded in to 3 parts then i will draw that circle with 3 diffrent color's..means that 3 arcs forma complete circle
there is a tutorial from pixelwit
that shows how to draw an arc
Draw your color regions, then apply circle mask to them:
//draw two colored stripes
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.beginFill(0xFF0000);
g.drawRect(0, 0, 100, 50);
g.endFill();
g.beginFill(0xFFFF00);
g.drawRect(0, 50, 100, 50);
g.endFill();
//create circle mask
var mask:Shape = new Shape();
g = mask.graphics;
g.beginFill(0xFFFFFF);
g.drawCircle(50, 50, 50);
g.endFill();
//now shape is two-colored circle
shape.mask = mask;