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();
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.)
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'm trying to create a grid (matrix) of squares in a given area <canvas> with n columns and m rows, and an option to set a spacing between the squares if desired. The grid should fill squares with random coloring.
I'd also like to add a zoom function, so the colored squares will appear much bigger in a region that is double-clicked.
Can anyone suggest a good method to generate the grid and assign random colors to the squares? If you can suggest how to create the zoom effect too that would be SUPER :)
I'm new to canvas, so any method help would be great!
Thanks
It looks like from the comments you #Spencer Wieczorek have the padded-cell drawing worked out.
The key to the other part of your question is using transforms:
determine the point from which you want to scale (doubleclicking): var scalePtX,scalePtY.
save the untransformed context state: ctx.save();
translate by (1-zoom)*scalePoint: ctx.translate((1-zoom)*scalePtX,(1-zoom)*scalePtY)
scale by the zoom: ctx.scale(zoom,zoom)
draw cells using coordinates as if they were untransformed: fillRect
restore the context to its untransformed state: ctx.restore()
Here's the important code and a Demo: http://jsfiddle.net/m1erickson/e8bfg3h4/
function draw(){
ctx.clearRect(0,0,cw,ch);
ctx.save();
ctx.translate((1-zoom)*scalePtX,(1-zoom)*scalePtY);
ctx.scale(zoom,zoom);
ctx.globalAlpha=0.25;
for(var y=0;y<rows;y++){
for(var x=0;x<cols;x++){
ctx.fillStyle=colors[y*cols+x];
ctx.fillRect(x*(w+padding),y*(h+padding),w,h);
}}
ctx.globalAlpha=1.00;
ctx.beginPath();
ctx.arc(scalePtX,scalePtY,10,0,Math.PI*2);
ctx.closePath();
ctx.stroke();
ctx.restore();
}
Lets say I draw a rectangle on the html canvas:
draw.rect(x, y, w, h, color); // color red
After I draw the rectangle I will draw a circle on the same canvas:
draw.circle(x, y, d, color); // color green
I have to randomly generate the coordinates for the circle.
Both draw functions are inside a loop - set interval - and a clear canvas function.
I am wondering if there is a way to make sure I won't draw the circle over the rectangle.
In a normal situation that would be easy, just remember the last coordinates of the rectangle and choose different ones for the circle - but for other reasons I cannot do it.
Would it be possible to check the canvas for the color of the rectangle which was drew on it, and make sure the circle will not be drawn over that color?
I know how to analyze the color of a background image, but I don't know if the above is possible.
For background images I use:
ctx.getImageData()
You will always be able to store the last drawn coordinates.
At least by using a glval var. which you can make not-so ugly
by using a namespace :
window.myApp = {};
myApp.lastDrawnRect = { x:-1, y:0, w:0, h:0 };
myApp.storeRect= function(x,y,w,h) {
var rect = myApp.lastDrawnRect;
rect.x = x; rect.y = y; rect.w = w;
}
and when you draw your rect you can store the coordinates :
raw.rect(x, y, w, h, color); // color red
myApp.storeRect(x,y,w,h);
You might want to store that the rectangle is not drawn by
taking the convention that x==-1 ===> rectangle cleared.
Then you can use that data when you draw your circle, with
classic boundary checking.
Whatever is drawn on the canvas is accessible using getImageData.
A canvas is a pixel matrix with drawing helpers attached.
Before drawing the circle you can ctx.getImageData(x, y, 1, 1) and check if it's red.
You most probably have to check the pixels on the edge of the circle, not the center.
Start from the circle equation.
It's been discussed before.
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