HTML Canvas draw arc between two points - javascript

I have found similar questions out there, but no answer. I have sketched a circle like so
ctx.strokeStyle='rgb(0,0,0)';
ctx.lineWidth=10;
ctx.beginPath();
ctx.arc(100,100,45,0,Math.PI*2,true);
ctx.closePath();
ctx.stroke();
which gives a circle situated at (100,100) with radius 45, plus 5 for the linewidth, making it a circle of radius 50. Now, I want to sketch the exact same circle, but another color, and only 1/4 of the original circumfrance (think the XBOX 360 red ring of doom). So I tried this
ctx.strokeStyle='rgb(0,250,0)';
ctx.lineWidth=10;
ctx.beginPath();
ctx.arc(100,100,45,0,Math.PI/2,true); //use 1/4 of original angle
ctx.closePath();
ctx.stroke();
But that has the really annoying aspect of connecting the first and last points (sometimes I wonder who created the canvas element, like when embedding text, but don't get me started on that...)

I've commented out the line you don't want. By calling closePath(), you are closing the path of your arc.
Example
JavaScript
ctx.strokeStyle='rgb(0,250,0)';
ctx.lineWidth=10;
ctx.beginPath();
ctx.arc(100,100,45,0,Math.PI/2,true); //use 1/4 of original angle
//ctx.closePath();
ctx.stroke();
jsFiddle.

Related

Make canvas fill include stroke in shapes

Ok, so i am making a 3D rendering engine in pure javascript, as a challenge of course - to test my linear algebra skills. I am not using webgl, so please do not say "use webgl".
Anyways, the software will take in triangles, a camera and local transformations, and render the data onto the screen (i even made it interactive)
There are only 6 lines of rendering code, however, which are:
// some shading and math calculations then this:
context.fillStyle = color;
context.strokeStyle = color;
context.beginPath();
context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x0, y0);
context.closePath();
context.fill();
context.stroke();
And while that works, it drops to 10fps with 4k+ faces on my Chromebook. (60fps on a regular computer)
Anyways, that outputs this:
But to make it faster, and because canvas state changes are slow, i removed the stroke, making the rendering code:
// some shading and math calculations then this:
context.fillStyle = color;
//context.strokeStyle = color;
context.beginPath();
context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x0, y0);
context.closePath();
context.fill();
//context.stroke();
which runs twice as fast, but the resulting thing that gets rendered to the screen is this: (different model)
which has ugly lines everywhere at the edges of the triangles (which get removed when I re-add the stroke)
However, the fps doubles and performance gains are great...
So i believe the lines are caused because the canvas fill doesnt include the area where it would have stroked (the outline, as you may say).
I have tried to fix it with math, and although it works there are some edge cases where it doesn't
So my question is as follows:
Is there a way to make the context fill include the stroke area without stroking, because it is very expensive?
Using both stroke and fill will force the rasterization twice which explains the approximate double time.
The reason why you get glitches between the triangles is because of rounding errors and anti-aliasing. There is not a straight-forward solution to this; the stroke will cover the glitches of course, but to do it without the stroke will require you to offset and expand at least every other triangle.
However, you could use a small trick to cover up the gap and that is to redraw the entire image (as bitmap) on top offset just a single pixel (you might get away with 0.5 pixel but then anti-aliasing is needed). This adds to the time, but far less than rasterization or recalculation of the paths.
Say that the result on the left is what you have (simulated here) with a clear gap. Redrawing it on top as shown in the right will cover the gap without too much distortion.
Simply use:
ctx.drawImage(sourceCanvas, 1, 1);
Tip: when only calling fill() you don't need closePath() as it is called implicit, saving one op. Microscopic gain perhaps but still (with more complex geometry it even might have an influence :) ).
Note: drawing to itself will cause an internal allocation of a temporary bitmap copy. However, you will only need to do one extra drawImage() operation. The option is to use off-canvas render but draw twice to a main displayed canvas. Either way...
var ctx = c.getContext("2d");
ctx.fillStyle = "#777";
tri(10,10, 72,17, 40.2, 100);
// simulates gap
ctx.fillStyle = "#222";
tri(72.5,17.5, 40.7,100.5, 90,25);
// fill entire image back again, drawn twice here for demo
ctx.drawImage(c, 100, 0);
ctx.drawImage(c, 0, 0, 100, 150, 101, 1, 100, 150);
ctx.fillText("Raster", 5, 8);
ctx.fillText("Offset self", 105, 8);
function tri(x0,y0,x1,y1,x2,y2) {
ctx.beginPath();
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.fill();
}
<canvas id=c></canvas>

How can I "colour" specific part of the screen using coordinates?

I'm just started working with Leap Motion (it is so much fun). The Leap works mainly with vectors. And now I want to create a program where I can visualise where is a vector pointing. The only way I can imagine doing this is by using a small image which appears when this fuction is on and positioning by using the img.style.left , img.style.top instructions. Any other ideas?
If your goal is to represent 2D Vectors,
You can use canvas to draw lines.
A canvas is like a div but you can draw whatever you want in it, I don't know anything about Leap Motion but if you want to draw lines and circles at precise coordinates, it may be a good solution instead of working with the DOM itself.
The JS part looks like this :
var canvas = document.getElementById('my-canvas');
var ctx = canvas.getContext('2d');
//For exemple here is how to draw a rectangle
//fillStyle support all valid css color
ctx.fillStyle = "rgba(50, 255, 24, 0.7)";
//Create the rectangle, with (startX, startY, height, width)
ctx.fillRect(20, 15, 50, 50);
ctx.beginPath(); //Tells canvas we want to draw
ctx.moveTo(250,250); //Moves the cursor to the coordinates (250, 250);
ctx.lineTo(75, 84); //Draws a line from the cursor (250, 250) to (75, 84);
ctx.closePath(); //Tells canvas to 'close' the drawing
ctx.strokeStyle = "red";
ctx.stroke(); //Draws the line stroke
And the HTML is simply :
<canvas id="my-canvas" height="500px" width="500px">
Here is the text displayed when the browser doesnt support canvas.
</canvas>
I made a jsfiddle to show you what simple things we can do with canvas.
http://jsfiddle.net/pq8g0bf0/1/
A nice website to learn canvas : http://www.html5canvastutorials.com/tutorials/html5-canvas-element/
Since it's javascript, you are free to do calculations for your vectors coordinates, addding eventListeners etc ...

Circle(ARC) gradient fill with canvas JS

I need to draw a dynamic donut chart - something similar to -
http://194.90.28.56/~dev1/t.jpg
The green part indicates the percentage (in this case 27%) - it must be dynamic.
I think I need to do something like - Android - How to draw an arc based gradient
But with JS..
Thanks.
Great question. Gradients along paths in canvas are hard. The easiest way is to fudge it.
Instead of thinking of your image as a gradient that follows a circular path, think of it as two linear gradients.
One on the left side, going from green to gray, top to bottom.
The other on the right side, going from white to gray, top to bottom.
Imagine a square made of those two gradients:
Now imagine a circle cutting through:
That's all you gotta do.
To "cut" through like that its easiest to use clipping regions, so I've made an example doing that.
Here's the live example: http://jsfiddle.net/simonsarris/Msdkv/
Code below! Hope that helps.
var greenPart = ctx.createLinearGradient(0,0,0,100);
greenPart.addColorStop(0, 'palegreen');
greenPart.addColorStop(1, 'lightgray');
var whitePart = ctx.createLinearGradient(0,0,0,100);
whitePart.addColorStop(0, 'white');
whitePart.addColorStop(1, 'lightgray');
var width = 20;
ctx.lineWidth = width;
// First we make a clipping region for the left half
ctx.save();
ctx.beginPath();
ctx.rect(-width, -width, 50+width, 100 + width*2);
ctx.clip();
// Then we draw the left half
ctx.strokeStyle = greenPart;
ctx.beginPath();
ctx.arc(50,50,50,0,Math.PI*2, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default
// Then we make a clipping region for the right half
ctx.save();
ctx.beginPath();
ctx.rect(50, -width, 50+width, 100 + width*2);
ctx.clip();
// Then we draw the right half
ctx.strokeStyle = whitePart;
ctx.beginPath();
ctx.arc(50,50,50,0,Math.PI*2, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default

drawing a path and then an image in html5

I'm trying to draw a circle and then have an image follow the circle around. Later I want to rotate and move the image around with respect to the circle drawn. The problem I'm facing is that when I try to rotate the image it won't rotate. It also doesn't show me an error in the console. I have functions allowing me to move the circle around and the image moves with it, I just can't seem to rotate the image.
Here is the code:
draw: function(){
//draw self on canvas;
//intended only to be called from update, should never
//need to be deliberately called
ctx = this.context;
ctx.save();
ctx.fillStyle="#000000";
ctx.beginPath();
//void arc(double x, double y,
// double radius, double startAngle, double endAngle,
// optional boolean anticlockwise = false);
ctx.arc(this.x,this.y,this.size,0,Math.PI*2,true);
ctx.closePath();
ctx.fill();
//ctx.translate(this.x, this.y);
ctx.rotate(this.imgAngle);
//draw the hammer
ctx.drawImage(this.hammer,this.hammerX,this.hammerY,100,100)
ctx.rotate(Math.PI/2);
ctx.restore();
},
Live Demo
Try changing your code to the following. You need to perform the rotation before drawing. You can translate the canvas to the entities position and then draw the image at x:0,y:0 to get the effect you desire. Note I did 0-50,0-50 because that puts the point of origin in the center since the width and height are 100. Meaning your image will rotate around its center rather than around its corner.
//draw the hammer
ctx.translate(this.hammerX, this.hammerY);
ctx.rotate(this.imgAngle);
ctx.drawImage(this.hammer,0-50,0-50,100,100);
The rotation will only affect drawings made AFTER the rotation is done.
You could try moving the rotate calls to just before the object that needs to be rotated?

clear pixels under a shape in HTML canvas

I am using an HTML canvas and javascript and I need to clear all of the pixels underneath a shape created by closing a path (for example, I am using flot, and I want to make rounded corners, and to do this, I first need to remove the square corners by drawing a curve on top of the corner to remove the desired pixels).
Right now, I am doing this by just filling the shape with the same color as the background, which can imitate what I want to do, but, it is not ideal as it makes it impossible to place the chart on top of non-solid backgrounds without seeing the square corners. I know that there is a clearRect method that would do what I want to do, but with only rectangles, I need to do it with any closed shape. Is it possible, and if so, how would I do it?
brainjam's code was heading in the right direction, but didn't fully solve the problem. Here's the solution:
context.save();
context.globalCompositeOperation = 'copy';
context.fillStyle = 'rgba(0,0,0,0)';
//draw shape to cover up stuff underneath
context.fill();
context.restore();
Here's an example of a function that will clear a circle from a canvas:
var clearCircle = function(x, y, radius)
{
context.save();
context.globalCompositeOperation = 'destination-out';
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
context.fill();
context.restore();
};
I think what you want is a clipping region, defined by the clip() function. The latter takes a bunch of paths. Here's an example.
This is a little different from what you are specifically asking (which is to remove pixels after drawing them), but actually not drawing the pixels in the first place is probably better, if I understand your requirements correctly.
Edit: I now think I understand that what you want to do is clear pixels to transparent black. To do that, after having defined your paths, do something like this:
context.fillStyle = 'rgba(0,0,0,0)';
context.fill();
The first statement sets the fill color to transparent black.
Use globalCompositeOperation = 'destination-out' instead of 'copy', it will erase all pixels of the shape in the canvas.
See all kinds of composition here
very usefull !

Categories

Resources