Canvas Chart fill the space between lines - html

I'm developing a simple chart using html5 Canvas. The idea is to draw two lines - one for min values and another one for max values, which I managed to do AND fill the space between those two line with some colour. Wonder how do I do the last part?

to fill the space you have to:
//get the context of the canvas
var context = canvas.getContext("2d");
//begin to draw
context.beginPath();
//draw all the lines you need to do the path....
context.moveTo(x, y);
context.lineTo(x1,y1);
context.lineTo(x2,y2);
//end of draw
context.closePath();
//to fill the space in the shape
context.fillStyle = "#FF00FF";
context.fill();
//to draw a border
context.lineWidth = 5;
context.strokeStyle = "#FF0000";
context.stroke();
UPDATE: to fill the space between 2 lines, is drawing a square:
I asume the lines are defined as:
line1: from (x1,y1) to (x2,y2)
line2: from (x3,y3) to (x4,y4)
then the square to draw to fill the space:
from (x1,y1) -> (x2,y2) -> (x3,y3) -> (x4,y4) and closepath();
then:
context.beginPath();
context.moveTo(x1,y1); //go to first point
context.lineTo(x2,y2); //draw to second point
context.lineTo(x3,y3); //draw to third point
context.lineTo(x4,y4); //draw to last point
context.closePath(); //draw to first point
context.fill(); //fill the area

Related

HTML5 circle and arrow with gradient background

I need a function in HTML5 that with given context, x1, y1, x2, y2 would draw me a circle (at beginning of this vector) with arrow that represents the vector itself, that would have gradient background. Left image is what I got - I am not pleased with the result... right image is visualisation of what I would like to make.
Could anybody help me with that please?
This is my arrow function (circle is obvious):
function arrow(context, fromx, fromy, tox, toy){
var headlen = 10;
var angle = Math.atan2(toy-fromy,tox-fromx);
context.moveTo(fromx, fromy);
context.lineTo(tox, toy);
context.lineTo(tox-headlen*Math.cos(angle-Math.PI/6),toy-headlen*Math.sin(angle-Math.PI/6));
context.moveTo(tox, toy);
context.lineTo(tox-headlen*Math.cos(angle+Math.PI/6),toy-headlen*Math.sin(angle+Math.PI/6));
}
Ok, I'll bite...
If your arrow function is working fine now, you can also draw a circle in that function.
This modified arrow() will draw both the circle and arrow in gray.
Since you want to change the context state ( change fill/stroke to gray ) you should wrap the path drawing commands in context.save and context.restore.
You should (must!) begin all atomic path drawings with context.beginPath. This prevents path drawings from being inadvertently repeated.
This refactoring of your arrow uses context transforms to set the rotation point at x1,y1 and then draws the circle and then draws the arrow to x2,y2.
A Demo: http://jsfiddle.net/m1erickson/87T8d/
Example arrow function:
function arrow(x1,y1,x2,y2){
var dx=x2-x1;
var dy=y2-y1;
var radians=Math.atan2(dy,dx);
var length=Math.sqrt(dx*dx+dy*dy);
// save the unrotated context state
ctx.save();
// set the rotation point as x1,y1
ctx.translate(x1,y1);
// rotate the canvas by the angle of the vector
ctx.rotate(radians);
// draw the circle
ctx.beginPath();
ctx.arc(0,0,8,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle="gray"; // or gradient if you prefer
ctx.fill();
// draw the arrow
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(length,0);
ctx.lineTo(length-7,-4);
ctx.lineTo(length-7,4);
ctx.lineTo(length,0);
ctx.closePath();
ctx.fillStyle="gray";
ctx.fill();
ctx.strokeStyle="gray";
ctx.stroke();
// restore the context to its unrotated state
ctx.restore();
}

CSS Radial Clip

I have a image of a circle (png) and I'd like to use clip to mask a portion of it. Is there such a type of radial clip? (Say I wanted to convert the circle to a pie chart e.g 32% only) with the clip mask
Edit: Perhaps tips on a SVG overlay would be nice if this isnt possible?
Using canvas (and a very small bit of jquery to get node references):
var ctx = $( '#canvasTag' )[0].getContext( '2d' );
// draw your image
ctx.drawImage( $( '#imgTag' )[0], x, y );
// change composite operation so that only the image below the arc below shows
ctx.globalCompositeOperation = 'destination-in';
// draw part of a circle
ctx.beginPath();
ctx.arc( x, y, radius, startAngle, endAngle, false ); // draw outer arc
ctx.lineTo( x, y ); // draw to center
ctx.closePath();
ctx.fill();

Draw single pixel line in html5 canvas

When i try to draw single pixel black line with the following code:
context.strokeStyle = '#000';
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.lineWidth = 1;
context.stroke();
context.closePath();
I have more then one pixel line with gray border. How to fix it?
Here is an example http://jsfiddle.net/z4VJq/
Call your function with these coordinates instead: drawLine(30,30.5,300,30.5);. Try it in jsFiddle.
The problem is that your color will be at an edge, so the color will be halfway in the pixel above the edge and halfway below the edge. If you set the position of the line in the middle of an integer, it will be drawn within a pixel line.
This picture (from the linked article below) illustrates it:
You can read more about this on Canvas tutorial: A lineWidth example.
You have to use context.translate(.5,.5); to offset everything by half a pixel. Its easy way for fix your problem
var canvas = document.getElementById("canvas1");
var context1 = canvas.getContext('2d');
context1.strokeStyle = '#000';
context1.beginPath();
context1.moveTo(10, 5);
context1.lineTo(300, 5);
context1.stroke();
var canvas2 = document.getElementById("canvas2");
var context2 = canvas2.getContext('2d');
context2.translate(.5,.5);
context2.strokeStyle = '#000';
context2.beginPath();
context2.moveTo(10, 5);
context2.lineTo(300, 5);
context2.stroke();
<div><canvas height='10' width='300' id='canvas1'>Обновите браузер</canvas></div>
<div><canvas height='10' width='300' id='canvas2'>Обновите браузер</canvas></div>

HTML5 - Canvas Shape Stroke

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/

Canvas 'Clip' reverse action?

Assuming I have:
var context = document.getElementById('test').getContext('2d');
// Background
context.fillStyle = '#000';
context.fillRect(0,0,300,300);
// 'P'
context.beginPath();
context.moveTo(90,89);
context.lineTo(161,89);
context.quadraticCurveTo(200,89,200,127);
context.quadraticCurveTo(200,166,148,166);
context.lineTo(115,166);
context.lineTo(108,210);
context.lineTo(69,210);
context.lineTo(90,89);
context.fillStyle = "#eee";
context.fill();
context.closePath();
context.globalCompositeOperation = 'destination-out';
// 'P' hole
context.beginPath();
context.moveTo(124,117);
context.lineTo(146,117);
context.quadraticCurveTo(160,117,160,127);
context.quadraticCurveTo(160,145,146,145);
context.lineTo(120,145);
context.lineTo(124,117);
context.fillStyle = '#ffffff';
context.fill();
context.closePath();
This works, except as you can see the 'hole' isn't a clip, so if the background isn't solid, you simply can't set the 'fill' color of the hole to match the background.
Therefore I need to clip the hole instead. When I do that however, the only part of the 'P' that shows is the part bound by the clip 'hole'. I need the reverse. I need the 'P' to show, but clip the part with the 'hole' so any background will show through.
Here is as far as I got, but not quite there:
var context = document.getElementById('test').getContext('2d');
// Background
context.fillStyle = '#000';
context.fillRect(0,0,300,300);
// 'P' hole clip
context.beginPath();
context.moveTo(124,117);
context.lineTo(146,117);
context.quadraticCurveTo(160,117,160,127);
context.quadraticCurveTo(160,145,146,145);
context.lineTo(120,145);
context.lineTo(124,117);
context.clip();
context.closePath();
// 'P'
context.beginPath();
context.moveTo(90,89);
context.lineTo(161,89);
context.quadraticCurveTo(200,89,200,127);
context.quadraticCurveTo(200,166,148,166);
context.lineTo(115,166);
context.lineTo(108,210);
context.lineTo(69,210);
context.lineTo(90,89);
context.fillStyle = "#eee";
context.fill();
context.closePath();
Thank you for your help!
I understand you asked this a while ago but I have an answer for you.
Your first example was half-correct. Using destination-out will work, however in order not to disturb the canvas you want to draw on, we create a new canvas and draw it in that.
Then once we've drawn our shape on there with our cutaways, we then draw the entire canvas onto the original. Since the new canvas has no background it will keep transparency.
var canvas = document.getElementById('test'),
context = canvas.getContext('2d'),
// New canvas - we will draw the letter P on here
newCanvas = document.createElement('canvas'),
newContext = newCanvas.getContext('2d');
// Make sure you have enough room on your new canvas
newCanvas.width = canvas.width;
newCanvas.height = canvas.height;
with(newContext) {
// 'P'
beginPath();
moveTo(90,89);
lineTo(161,89);
quadraticCurveTo(200,89,200,127);
quadraticCurveTo(200,166,148,166);
lineTo(115,166);
lineTo(108,210);
lineTo(69,210);
lineTo(90,89);
fillStyle = "#eee";
fill();
closePath();
globalCompositeOperation = 'destination-out';
// 'P' hole
beginPath();
moveTo(124,117);
lineTo(146,117);
quadraticCurveTo(160,117,160,127);
quadraticCurveTo(160,145,146,145);
lineTo(120,145);
lineTo(124,117);
fillStyle = '#000';
fill();
closePath();
}
with(context) {
// Background
fillStyle = '#000';
fillRect(0,0,300,300);
// Simply reference the canvas element when drawing
drawImage(newCanvas, 0, 0);
}
I know this is very old but... you are not using clip correctly in your second try. Everything that comes after clip will be drawn only on the clipped area.
so you could either:
Clip the hole and redraw the background
var context = document.getElementById('test').getContext('2d');
// Background
context.fillStyle = '#000';
context.fillRect(0,0,300,300);
// 'P'
context.beginPath();
context.moveTo(90,89);
context.lineTo(161,89);
context.quadraticCurveTo(200,89,200,127);
context.quadraticCurveTo(200,166,148,166);
context.lineTo(115,166);
context.lineTo(108,210);
context.lineTo(69,210);
context.lineTo(90,89);
context.fillStyle = "#eee";
context.fill();
// 'P' hole clip
context.beginPath();
context.moveTo(124,117);
context.lineTo(146,117);
context.quadraticCurveTo(160,117,160,127);
context.quadraticCurveTo(160,145,146,145);
context.lineTo(120,145);
context.lineTo(124,117);
context.clip();
// Redraw Background in the clipped area
context.fillStyle = '#000';
context.fillRect(0,0,300,300);
<canvas id="test" width="300" height="300">
clip the outside part of the hole and draw the P
var context = document.getElementById('test').getContext('2d');
// Background
context.fillStyle = '#000';
context.fillRect(0,0,300,300);
// inverse 'P' hole clip
context.beginPath();
context.rect(0, 0, 300, 300);
context.lineTo(124,117);
context.lineTo(120,145);
context.lineTo(146,145);
context.quadraticCurveTo(160,145,160,127);
context.quadraticCurveTo(160,117,146,117);
context.lineTo(124,117);
context.clip();
// 'P'
context.beginPath();
context.moveTo(90,89);
context.lineTo(161,89);
context.quadraticCurveTo(200,89,200,127);
context.quadraticCurveTo(200,166,148,166);
context.lineTo(115,166);
context.lineTo(108,210);
context.lineTo(69,210);
context.lineTo(90,89);
context.fillStyle = "#eee";
context.fill();
<canvas id="test" width="300" height="300">
Also, you don't need to use closePath here.