i currently have the following:
ctx.save();
ctx.translate(459,258);
ctx.rotate(sec * Math.PI/36);
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(135,0);
ctx.lineTo(210,0);
ctx.strokeStyle = 'rgba(0,153,255)';
ctx.stroke();
ctx.restore();
drawing a circle of lines (stroke). I want to convert it into a clearRect so that instead of drawing this circle of lines, i want it to erase a circle in the same shape... how can i do this?
i tried doing this but it did not work:
ctx.save();
ctx.translate(459,258);
ctx.rotate(sec * Math.PI/36);
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(135,0);
ctx.lineTo(210,0);
ctx.clear();
ctx.restore();
thanks.
Related
I am drawing five horizontal lines to an HMTL 5 2D canvas:
var canvas_ctx = my_canvas.getContext("2d");
canvas_ctx.lineWidth = 0.5;
canvas_ctx.strokeStyle = "black";
{
let line_x = 0;
let line_length = canvas_ctx.width;
let offset = 5;
let numLines = 5;
let numYincrement = 10;
for (let i=0;i<numLines * numYincrement;i+=numYincrement) {
//canvas_ctx.beginPath();
canvas_ctx.moveTo(line_x,i + offset);
canvas_ctx.lineTo(line_length,i + offset);
canvas_ctx.stroke();
//canvas_ctx.closePath();
}
}
This should, ideally result in 5 black lines. Instead, the color of the lines seems to fade with each new line (as if it's a gradient!), so that line 5 is gray. If I uncomment canvas_ctx.beginPath(); and canvas_ctx.closePath();, all lines become gray. Why is this happening??
Strokes do overlap from both sides of the coordinates.
var ctx = c.getContext('2d');
ctx.strokeStyle="red";
// draw big
ctx.scale(30, 30);
ctx.beginPath();
ctx.moveTo(5, 0);
ctx.lineTo(5, 10);
ctx.stroke();
drawPixelGrid();
function drawPixelGrid() {
// simply renders where the pixel bounds are
ctx.beginPath();
// remove the zoom
ctx.setTransform(1,0,0,1,0,0);
ctx.strokeStyle = 'gray';
ctx.lineWidth = 2; // avoid the problem we are demonstrating by using a perfect lineWidth ;-)
for(let y=0; y<=300; y+=30) {
ctx.moveTo(0, y);
ctx.lineTo(300, y);
for(let x=0; x<=300; x+=30) {
ctx.moveTo(x, 0);
ctx.lineTo(x, 300);
}
}
ctx.stroke();
}
<canvas id="c" height=300></canvas>
But obviously, a pixel can't be set to two colors at the same time. So browsers apply antialiasing, which will fade your pixel color to an other color, being the result of mixing the background and the foreground color.
So for a black stroke over a white or transparent background, this leads to actual gray pixels being rendered. Here I'll keep using red as an example:
var ctx = c.getContext('2d');
ctx.strokeStyle="red";
// first draw as on a 10*10 canvas
ctx.beginPath();
ctx.moveTo(5, 0);
ctx.lineTo(5, 10);
ctx.stroke();
// zoom it
ctx.imageSmoothingEnabled = 0;
ctx.globalCompositeOperation = 'copy';
ctx.drawImage(c, 0,0,9000,9000);
drawPixelGrid();
// this is not red...
function drawPixelGrid() {
ctx.globalCompositeOperation = 'source-over';
ctx.beginPath();
ctx.setTransform(1,0,0,1,0,0);
ctx.strokeStyle = 'gray';
ctx.lineWidth = 2;
for(let y=0; y<=300; y+=30) {
ctx.moveTo(0, y);
ctx.lineTo(300, y);
for(let x=0; x<=300; x+=30) {
ctx.moveTo(x, 0);
ctx.lineTo(x, 300);
}
}
ctx.stroke();
}
<canvas id="c" height=300></canvas>
One way to avoid it is generally to apply an offset on your coordinates so that the line extends correctly on pixels boundaries. E.g for a 1px lineWidth, you would apply a 0.5 offset:
var ctx = c.getContext('2d');
ctx.strokeStyle="red";
// first draw as on a 10*10 canvas
ctx.beginPath();
ctx.moveTo(5.5, 0); // offset +0.5px
ctx.lineTo(5.5, 10);
ctx.stroke();
// zoom it
ctx.imageSmoothingEnabled = 0;
ctx.globalCompositeOperation = 'copy';
ctx.drawImage(c, 0,0,9000,9000);
drawPixelGrid();
// now we've got a real red
function drawPixelGrid() {
ctx.globalCompositeOperation = 'source-over';
ctx.beginPath();
ctx.setTransform(1,0,0,1,0,0);
ctx.strokeStyle = 'gray';
ctx.lineWidth = 2;
for(let y=0; y<=300; y+=30) {
ctx.moveTo(0, y);
ctx.lineTo(300, y);
for(let x=0; x<=300; x+=30) {
ctx.moveTo(x, 0);
ctx.lineTo(x, 300);
}
}
ctx.stroke();
}
<canvas id="c" height=300></canvas>
But in your case, you are drawing at 0.5px lineWidth, so no offset will be able to get rid of this antialiasing.
So if you want perfect color, choose a correct lineWidth.
How do I empty the data in my ctx?
Basically the second stroke() should do anything, but draws another triangle. How do I clear this triangle from ctx?
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.save();
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(20, 100);
ctx.lineTo(70, 100);
ctx.closePath();
ctx.stroke();
ctx.stroke();//First stroke
ctx.restore();//Does not empty line paths?
ctx.strokeStyle = "green";
ctx.stroke();//Seccond stroke, same triangle drawn, but green (I do not want to draw the triangle)
https://jsfiddle.net/dh3e5wg6/
I am trying to using context.clip() to clip a draw arc from other one and fill the clipped result.
But when i clip section & fill it, it gives pixelated fill .
var ctx = document.getElementById("canvas").getContext("2d");
var x = 150 ;
var y = 150 ;
var r = 100 ;
ctx.save() ;
ctx.translate(x,y) ;
ctx.beginPath() ;
ctx.arc(0,0,r,0,2*Math.PI);
ctx.closePath() ;
ctx.fillStyle = "cyan" ;
ctx.fill() ;
ctx.lineWidth = 10;
ctx.stroke();
ctx.restore() ;
ctx.save() ;
ctx.clip() ;
ctx.translate(x,y);
ctx.beginPath();
ctx.moveTo(r,-r-10);
ctx.arc(0,-r-10,r,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle = "#f2f2f2";
ctx.fill();
ctx.lineWidth = 1;
ctx.stroke();
ctx.restore();
https://jsfiddle.net/x0d0n40z/1/
An alternative approach which eliminates the need for clip()/save()/restore() is to use a few steps of compositing.
Clipping mask is anti-aliased in some browsers while in other not. To obtain consistency (and in some cases also performance since save-clip-restore are relative expensive operations) using composition is preferred if possible.
In this case:
Fill main arc in target color
Define a clipping arc
Change composite mode to destination-out and fill (will cut main)
Change composite mode to source-atop and stroke (will outline cut)
Change composite mode to source-over and stroke outline of main circle
Example
Update: Simplified steps (with the last step merged into the process, ref. comments). I also chose to demonstrate use of the Path2D since we can reuse the object without interfering with the path on the ordinary context -
var ctx = c.getContext("2d"),
p = new Path2D(), // this will store main shape for reuse
x = 75, y = 75, radius = 70;
// main arc
p.arc(x, y, radius, 0, 6.28); // store to path object
ctx.fillStyle = "cyan";
ctx.fill(p); // fill path object
// clip top arc
ctx.globalCompositeOperation = "source-atop";
ctx.arc(x, y - radius, radius, 0, 6.28);
ctx.fillStyle = "#09f";
ctx.fill();
ctx.lineWidth = 5;
ctx.stroke();
// stroke main arc
ctx.globalCompositeOperation = "source-over";
ctx.stroke(p); // stroke path object
body {background:#e9e9e9}
<canvas id=c></canvas>
Old version:
var ctx = c.getContext("2d"),
x = 75, y = 75, radius = 70;
// main arc
ctx.arc(x, y, radius, 0, 6.28);
ctx.fillStyle = "cyan";
ctx.fill();
// clipping arc
ctx.beginPath();
ctx.arc(x, y - radius, radius, 0, 6.28);
// cut step
ctx.globalCompositeOperation = "destination-out";
ctx.fill();
// stroke gap step
ctx.globalCompositeOperation = "source-atop";
ctx.lineWidth = 10;
ctx.stroke();
// stroke whole outline
ctx.globalCompositeOperation = "source-over";
ctx.beginPath();
ctx.arc(x, y, radius, 0, 6.28);
ctx.lineWidth = 5;
ctx.stroke();
// if you want to color the clip then use this:
ctx.globalCompositeOperation = "destination-atop";
ctx.fillStyle = "#09f";
ctx.fill();
body {background:#e9e9e9}
<canvas id=c></canvas>
The problem is that the clip boundary is not being anti alised.
To solve you can render the shape without using the clip. The ctx.arc method lets you set the start and end angles so you can get the inset by filling two arcs.
You will need to get the angles where the clip circle and the inset circle intercept.
For this case it is very simple. First get the distance between the circles, and the angle from one to the other. This works only for two circles of same radius.
var c = {x:?,y:?}; // circle one location
var c1 = {x:?,y:?}; // circle two location
var radius = ?; // radius of both
var angle = Math.atan2(c1.y - c.y, c1.x - c.x); // get the angle from one to the next
var dist = Math.hypot(c1.x - c.x, c1.y - c.y); // get the distance. NOTE IE does not have hypot so do it the normal way with Math.sqrt....
Now you have the angle and distance the intercepts are a simple relationship between the distance and the radius
var iAngle = Math.acos(dist / 2 / radius); // the angle from the line between the circles
// to the intercepts
Now you have that angle you can draw the two arcs
ctx.beginPath();
ctx.arc(c.x,c.y,radius,angle - iAngle , angle + iAngle); // first arc
ctx.arc(c1.x,c1.y, radius, angle + Math.PI - iAngle, angle + Math.PI + iAngle); // second arc
ctx.fill();
ctx.stroke();
There is not much you can do to prevent the jaggies from effecting the clip area. Another way to achieve clipping is to use ctx.globalCompositeOperation to render a mask. You can mask in and out, and many more options. This will be a better solution when the clipping area becomes more complex.
I finally figured the right way to correct the bug .
Heres the clean result of what i wanted https://jsfiddle.net/x0d0n40z/6/
Code :
var ctx = document.getElementById("canvas").getContext("2d");
var r = 50
x = ctx.canvas.width/2;
y = ctx.canvas.height/2;
var offset = 60;
ctx.save();
ctx.setTransform(1,0,0,1.5,x,y);
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI);
ctx.stroke();
ctx.clip();
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI,false);
ctx.fillStyle = "cyan";
ctx.fill();
ctx.setTransform(1, 0, 0, 1, x, y);
ctx.beginPath();
ctx.arc(0,-offset,r,0,2*Math.PI,false);
ctx.fillStyle = "#f2f2f2";
ctx.fill();
ctx.lineWidth = 1 ;
ctx.stroke();
ctx.setTransform(1,0,0,1.5,x,y);
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI,false);
ctx.lineWidth = 3 ;
ctx.stroke();
ctx.restore();
Source from were i learned to use clip : http://www.html5canvastutorials.com/advanced/html5-canvas-clipping-region-tutorial/
I'm trying to draw several shapes to a canvas and I'm running into a strange issue. When I try to draw a white rectangle on the canvas before drawing my shape, the shape never shows up. What am I doing wrong?
Sample code:
// drawing a white background
// This breaks it. If I comment thiese two line out, it works.
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(0, 0, Canvas.width, Canvas.height);
// Draw the rectangle
ctx.lineWidth = LineWidth;
ctx.strokeStyle = Color;
ctx.beginPath();
var startPos = Ordinates[0];
ctx.moveTo(startPos.start.x, startPos.start.y);
ctx.lineTo(startPos.stop.x, startPos.stop.y);
for(var i = 0; i < Ordinates.length; i++){
if(i === 0) continue;
ctx.lineTo(Ordinates[i].stop.x, Ordinates[i].stop.y);
}
ctx.closePath();
ctx.fill();
And here's a fiddle.
If you comment out the 2 lines that draws the white background rectangle, it works fine. What am I doing wrong?
ctx.fillStyle = "#FFFFFF"; // <-- set the fill color to white
ctx.fillRect(0, 0, Canvas.width, Canvas.height);
// Draw the rectangle
ctx.lineWidth = LineWidth;
ctx.strokeStyle = Color;
ctx.beginPath();
var startPos = Ordinates[0];
ctx.moveTo(startPos.start.x, startPos.start.y);
ctx.lineTo(startPos.stop.x, startPos.stop.y);
for(var i = 0; i < Ordinates.length; i++){
if(i === 0) continue;
ctx.lineTo(Ordinates[i].stop.x, Ordinates[i].stop.y);
}
ctx.closePath();
ctx.fill(); // <--- filling with white, while the background is still. you can remove this line btw
ctx.fillStyle = '#000000'; // <-- now we set it to black
ctx.fill(); // <--- filling with black now
also, if you want to see an outline of the path:
ctx.stroke();
this draws the outline of it in the right color.
But in a reply to v.rouge you just said you wanted it white with a black outline:
ctx.fillStyle = '#ffffff';
ctx.strokeStyle = '#000000';
ctx.fill();
ctx.stroke();
done :-)
Fillstyle is white. Drawing is white. Might explain.
I want to draw a line with one color, and the next line with a different color. But when I call stroke() the second time, the first line is draw again! How can I avoid it? Here's my code:
var canv = document.getElementById("canvas");
var ctx = canv.getContext("2d");
ctx.moveTo(0,0);
ctx.lineTo(100,100);
ctx.strokeStyle = "#FF0000";
ctx.stroke();
ctx.moveTo(100,100);
ctx.lineTo(100,200);
ctx.strokeStyle = "#999999";
ctx.stroke();
Thanks in advance!
Just insert a beginPath() in there:
var canv = document.getElementById("canvas");
var ctx = canv.getContext("2d");
ctx.moveTo(0,0);
ctx.lineTo(100,100);
ctx.strokeStyle = "#FF0000";
ctx.stroke();
cxt.beginPath(); // <---
ctx.moveTo(100,100);
ctx.lineTo(100,200);
ctx.strokeStyle = "#999999";
ctx.stroke();
That will reset your path. A stroke just strokes what exists on the path but does not clear it. You will have to manually reset the path for each new shape you want to draw.
The advantage is that you can reuse the path for fill, clip and point testing. The disadvantage is that it's easy to forget sometimes.