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
Related
Say I have drawn a circle on a canvas that has something else drawn on it that stops me from clearing the canvas - due to the other element being randomly generated
var circleX = 50;
var circleY = 10;
var moveCircX = 2;
var moveCircY = 3;
function createCirc(){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(circleX, circleY, 10, 0, Math.PI*2, true);
ctx.fill();
}
function circMove(){
circleY = (circleY + circMoveY)
//then validation to stop it from being drawn of the canvas
So what I'm trying to do is move the circle but clear the previous drawn circle from the canvas. So is there a solution to clearing the circle or would it be easier to create a sprite that replicates the circle?
Since your background isn't changing, the simplest strategy is to copy the background before you first draw your circle, then draw your circle. When you're moving, redraw that part of the background from the copy you kept, then draw your circle in the new place.
An efficient way to do that is to use getImageData and putImageData.
So, (my javascript is rusty, so this may not be perfect. Feel free to correct any mistakes), before the first time you createCirc, simply do:
imageData = ctx.getImageData(0,0, ctx.canvas.width, ctx.canvas.height)
And, in your circMove function, before you move and redraw the circle, you want:
ctx.putImageData(imageData, circleX, circleY, circleX, circleY, 2*circle_radius, 2*circle_radius)
(You don't define circle_radius, but I'm sure you must have a similar value. I'm using 2x the radius to presumably be the size of the image that is drawn.)
Ok I have an hard issue to solve. I have a HTML5 canvas in which I draw two charts (lines). I have the points of each chart where the lines are connected and I have two y-values (X,Y in the picture) where I have to draw a line and fill above or below the chart.
I really can't seem to get it work because I try coloring everything above the certain chart and clipping it with a rectangle but I have two chart so I must have two clipping areas which gives incorrect solution.
There is a picture attached to the post to see the case
So I have a red chart and a brown chart and values for X and Y (which are the colorful lines). X is the light blue - the height to where I want to color the graph below. Y is the light gray and is the height for coloring above the brown chart.
How can I implement this wihtout knowing the crossing point of the charts and X or Y?
The code I am using is this. I call it twice for every chart. I have omitted the code for drawing the chart - it is drawing using the "points" array.
unfortunately I don't have the points of the crossing between the end of the color area and the chart (the crossing of red and light blue ; brown and light gray)
ctx.rect(clipX, clipY, clipWidth, clipHeight);
ctx.stroke();
ctx.clip();
//fill the area of the chart above or below
ctx.globalAlpha = 0.4;
ctx.strokeStyle = color;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y + visibleGraphicSpace);
for (var i = 1; i < points.length; i++) {
ctx.lineTo(points[i].x, points[i].y + visibleGraphicSpace);
}
ctx.closePath();
ctx.fill();
First I draw rectangule for the visible area, then I draw the chart with the given points array, close it and fill everything above or below till the end of the canvas. But this solution only takes the second filling right because it overrides the first one.
PS: I need to draw both coloring fillings not only one of them.
I hope I managed to explain it well enough. If you have any questions don't mind to ask.
Thank you for the help in advance.
You can create a clipping area (using context.clip) to make sure your blue and gray fills are contained inside the paths created by your chart. When you set a clipping area, any new drawings will not be displayed outside the clipping area.
Save some chart points in an array.
Save the top & bottom range of fill within the charted points
Define the chart path (==plot the points without stroking the points)
Create a clipping area from the path (all new drawing will be contained inside the clipping area and will not appear outside the area)
Fill the clipping area (with your blue & gray fills)
Stroke the path (with your red and maroon strokes)
Note: When you create a clipping path, it can only be "unclipped" by wrapping the clipping code inside context.save & context.restore.
Here's annotated code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var pts0=[
{x:119,y:239},
{x:279,y:89},
{x:519,y:249},
{x:739,y:83},
{x:795,y:163},
];
var fill0={top:75,bottom:133};
var pts1=[
{x:107,y:342},
{x:309,y:523},
{x:439,y:455},
{x:727,y:537},
{x:757,y:389}
];
var fill1={top:473,bottom:547};
filledChart(pts0,fill0,'red','skyblue');
filledChart(pts1,fill1,'maroon','lightgray');
function filledChart(pts,fill,strokecolor,fillcolor){
// define the path
// This doesn't stroke the path, it just "initializes" it for use
ctx.beginPath();
ctx.moveTo(pts[0].x,pts[0].y);
for(var i=0;i<pts.length;i++){
var pt=pts[i];
ctx.lineTo(pt.x,pt.y);
}
// save the un-clipped context state
ctx.save();
// Create a clipping area from the path
// All new drawing will be contained inside
// the clipping area
ctx.clip();
// fill some of the clipping area
ctx.fillStyle=fillcolor;
ctx.fillRect(0,fill.top,cw,fill.bottom-fill.top);
// restore the un-clipped context state
// (the clip is un-done)
ctx.restore();
// stroke the path
ctx.strokeStyle=strokecolor;
ctx.lineWidth=2;
ctx.stroke();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<canvas id="canvas" width=800 height=550></canvas>
You could try to make the 'fill' before the chart.
You create the fill color.
You create the 'mask' (white color)
You create the chart line
An example, with only one chart, but can easily be changed to use two charts: https://jsfiddle.net/eLwc96fj/
var c2 = document.getElementById('test').getContext('2d');
// Create a colored rectangle
c2.fillStyle = '#0f0';
c2.rect(80,0, 200,70);
c2.fill();
// Create the 'mask' - it has the same path than the chart, but then follow the above rectangle.
c2.beginPath();
c2.fillStyle = '#fff';
c2.moveTo(80, 80);
c2.lineTo(120,50);
c2.lineTo(180, 90);
c2.lineTo(250, 40);
c2.lineTo(280, 120);
c2.lineTo(280, 0);
c2.lineTo(80, 0);
c2.closePath();
c2.fill();
// Draw the chart itself
c2.strokeStyle = '#f00';
c2.beginPath();
c2.moveTo(80, 80);
c2.lineTo(120,50);
c2.lineTo(180, 90);
c2.lineTo(250, 40);
c2.lineTo(280, 120);
c2.stroke();
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 ...
I have a roulette wheel created with HTML5 canvas and currently each slice is a plain color generated by using the fillStyle(), beginPath(), stroke(), then fill() methods.
I would like to use images that that crop appropriately in the shape of the slice, but I'm not sure how to implement this using drawImage().
Here's the jsfiddle
http://jsfiddle.net/pyD2q/2/
Any help or resources is appreciated.
Instead of filling the color in you can use it as a clipping mask instead. For each arc, set clip, draw the image and repeat.
Something like this (untested):
for (i = 0; i < s.members.length; i++) {
angle = s.startAngle + i * s.arc;
ctx.beginPath();
ctx.arc(s.width / 2, s.height / 2, s.outsideRadius, angle, angle + s.arc, false);
ctx.arc(s.width / 2, s.height / 2, s.insideRadius, angle + s.arc, angle, true);
ctx.save(); /// store current clip-state
ctx.clip(); /// set current arc as clipping mask
ctx.drawImage(someImageArray[i], x, y, width, height);
ctx.restore(); /// restore clip-state
ctx.stroke(); /// stroke arc
}
You will of course also need to place the images correctly relative to the section and if you want to rotate the image you would need to rotate it at this point.
In that regard I would recommend creating this wheel on an off-screen canvas. This way you only need to draw the off-screen canvas into the main canvas rotated and what have you (saves you the trouble of calculating each angle as well and the performance will be better).
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 !