Canvas quadraticCurveTo - javascript

I spotted this piece of code that generates a rectangle with rounded corners but I would like to be able to increase the size (height and width) of the rectangle as I want.
var canvas = document.getElementById('newCanvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(20, 10);
ctx.lineTo(80, 10);
ctx.quadraticCurveTo(90, 10, 90, 20);
ctx.lineTo(90, 80);
ctx.quadraticCurveTo(90, 90, 80, 90);
ctx.lineTo(20, 90);
ctx.quadraticCurveTo(10, 90, 10, 80);
ctx.lineTo(10, 20);
ctx.quadraticCurveTo(10, 10, 20, 10);
ctx.stroke();

You need to convert your static values to (x, y) coordinates and [width × height] dimension variables.
I took what you had and reverse-engineered the formulas to calculate your static drawing. Take your existing variables and change them to x or y and add the width or height to them and optionally add or subtract the radius where necessary.
const drawRoundedRect = (ctx, x, y, width, height, radius) => {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.stroke();
};
const canvas = document.getElementById('new-canvas');
const ctx = canvas.getContext('2d');
ctx.strokeStyle = 'black';
ctx.strokeRect(10, 10, 80, 80);
ctx.strokeStyle = 'red';
drawRoundedRect(ctx, 10, 10, 80, 80, 10);
ctx.strokeStyle = 'green';
drawRoundedRect(ctx, 20, 20, 60, 60, 14);
<canvas id="new-canvas"></canvas>

Don't forget to join path ends
I noticed that you forgot to close the path. This can result in a slight seam or bump at the start / end of the path depending on the ctx.lineJoin setting.
The call to ctx.closePath connects the end to the start with a line
Visual design
Visual design rules for the type of curves to use.
Beziers for curves that are part of things that move quickly
Circle for things that are static or move slowly
Bezier curves can never exactly fit a circle. Quadratic beziers are very bad fits. If you must use a bezier curve use a cubic bezier to get a better fit.
Best approximation of a circle using a cubic bezier is to inset control points by c = 0.55191502449 as fraction of radius. This will result in the minimum possible radial error of 0.019608%
Example shows the difference between a cubic (black) and quadratic (red) curves.
const ctx = canvas.getContext('2d');
ctx.strokeStyle = 'red';
drawRoundedRectQuad(ctx, 10, 10, 180, 180, 70);
ctx.strokeStyle = 'black';
drawRoundedRect(ctx, 10, 10, 180, 180, 70);
function drawRoundedRect(ctx, x, y, w, h, r) {
const c = 0.55191502449;
const cP = r * (1 - c);
const right = x + w;
const bottom = y + h;
ctx.beginPath();
ctx.lineTo(right - r, y);
ctx.bezierCurveTo(right - cP, y, right, y + cP, right, y + r);
ctx.lineTo(right, bottom - r);
ctx.bezierCurveTo(right, bottom - cP, right - cP, bottom, right - r, bottom);
ctx.lineTo(x + r, bottom);
ctx.bezierCurveTo(x + cP, bottom, x, bottom - cP, x, bottom - r);
ctx.lineTo(x, y + r);
ctx.bezierCurveTo(x, y + cP , x + cP, y, x + r, y);
ctx.closePath();
ctx.stroke();
}
function drawRoundedRectQuad(ctx, x, y, w, h, r){
ctx.beginPath();
ctx.lineTo(x + w- r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h- r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h- r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
ctx.stroke();
};
<canvas id="canvas" width ="200" height="200"></canvas>
Rounded corners to match CSS border-radius
To get a true rounded rectangle (circles rather than approx curve) use ctx.arc to create the rounded corners.
Extending the 2D API with roundedRect
The code below draws a rounded rectangle by adding the functions strokeRoundedRect(x, y, w, [h, [r]]), fillRoundedRect(x, y, w, [h, [r]]), and roundedRect(x, y, w, [h, [r]]) to the 2D context prototype.
Arguments
x, y, w, [h, [r]]
x, y Top left of rounded rectangle
w, Width of rounded rectangle
h, Optional height of rectangle. Defaults to value of width (creates rounded square)
r Optional radius or corners. Default is 0 (no rounded corners). If value is negative then a radius of 0 is used. If r > than half the width or height then r is change to Math.min(w * 0.5, h * 0.5)
Example
Including implementation of round rectangle extensions.
function Extend2DRoundedRect() {
const p90 = Math.PI * 0.5;
const p180 = Math.PI;
const p270 = Math.PI * 1.5;
const p360 = Math.PI * 2;
function roundedRect(x, y, w, h = w, r = 0) {
const ctx = this;
if (r < 0) { r = 0 }
if (r === 0) {
ctx.rect(x, y, w, h);
return;
}
r = Math.min(r, w * 0.5, h * 0.5)
ctx.moveTo(x, y + r);
ctx.arc(x + r , y + r , r, p180, p270);
ctx.arc(x + w - r, y + r , r, p270, p360);
ctx.arc(x + w - r, y + h - r, r, 0 , p90);
ctx.arc(x + r , y + h - r, r, p90 , p180);
ctx.closePath();
}
function strokeRoundedRect(...args) {
const ctx = this;
ctx.beginPath();
ctx.roundedRect(...args);
ctx.stroke();
}
function fillRoundedRect(...args) {
const ctx = this;
ctx.beginPath();
ctx.roundedRect(...args);
ctx.fill();
}
CanvasRenderingContext2D.prototype.roundedRect = roundedRect;
CanvasRenderingContext2D.prototype.strokeRoundedRect = strokeRoundedRect;
CanvasRenderingContext2D.prototype.fillRoundedRect = fillRoundedRect;
}
Extend2DRoundedRect();
// Using rounded rectangle extended 2D context
const ctx = canvas.getContext('2d');
ctx.strokeStyle = "#000";
ctx.strokeRoundedRect(10.5, 10.5, 180, 180); // no radius render rectangle
ctx.strokeRoundedRect(210.5, 10.5, 180, 180, 20); // Draw 1px line along center of pixels
ctx.strokeRoundedRect(20, 20, 160, 160, 30);
ctx.fillRoundedRect(30, 30, 140, 140, 20);
ctx.fillRoundedRect(230, 30, 140, 40, 20); // Circle ends
ctx.fillRoundedRect(230, 80, 140, 20, 20); // Auto circle ends
ctx.fillRoundedRect(280, 120, 40, 40, 120); // circle all sides
var inset = 0;
ctx.beginPath();
while (inset < 80) {
ctx.roundedRect(
10 + inset, 210 + inset,
380 - inset * 2, 180 - inset * 2,
50 - inset
);
inset += 8;
}
ctx.fill("evenodd");
<canvas id="canvas" width="400" height="400"></canvas>

Related

Fabric JS how to set border radius on bounding box of selected objects

Is it possible to change the border radius of a bounding box of a selected item? I have read through the documentation of the possible attributes that can be attributed to an object and haven't found anything that specifies the changing of the border radius of an object's bounding box. Is there perhaps an solution through CSS this can be done?
Here's a drop-in replacement for the fabric.Object.prototype.drawBorders method that handles the drawing of selection borders. I've extended it to use the property selectionRadius to determine the amount of border radius to use in the selection box.
var canvas = new fabric.Canvas("canvas");
canvas.add(new fabric.Rect({
width: 150,
height: 100,
left: 25,
top: 25,
fill: 'lightgreen',
strokeWidth: 0,
padding: 20,
selectionRadius: 20,
borderColor: 'red'
}));
fabric.Object.prototype.drawBorders = function(ctx, styleOverride) {
styleOverride = styleOverride || {};
var wh = this._calculateCurrentDimensions(),
strokeWidth = this.borderScaleFactor,
width = wh.x + strokeWidth,
height = wh.y + strokeWidth,
hasControls = typeof styleOverride.hasControls !== 'undefined' ?
styleOverride.hasControls : this.hasControls,
shouldStroke = false;
ctx.save();
ctx.strokeStyle = styleOverride.borderColor || this.borderColor;
this._setLineDash(ctx, styleOverride.borderDashArray || this.borderDashArray, null);
//start custom draw method with rounded corners
var rx = this.selectionRadius ? Math.min(this.selectionRadius, width / 2) : 0,
ry = this.selectionRadius ? Math.min(this.selectionRadius, height / 2) : 0,
w = width,
h = height,
x = -width / 2,
y = -height / 2,
isRounded = rx !== 0 || ry !== 0,
/* "magic number" for bezier approximations of arcs */
k = 1 - 0.5522847498;
ctx.beginPath();
ctx.moveTo(x + rx, y);
ctx.lineTo(x + w - rx, y);
isRounded && ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry);
ctx.lineTo(x + w, y + h - ry);
isRounded && ctx.bezierCurveTo(x + w, y + h - k * ry, x + w - k * rx, y + h, x + w - rx, y + h);
ctx.lineTo(x + rx, y + h);
isRounded && ctx.bezierCurveTo(x + k * rx, y + h, x, y + h - k * ry, x, y + h - ry);
ctx.lineTo(x, y + ry);
isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y);
ctx.closePath();
ctx.stroke();
//end custom draw method with rounded corners
if (hasControls) {
ctx.beginPath();
this.forEachControl(function(control, key, fabricObject) {
// in this moment, the ctx is centered on the object.
// width and height of the above function are the size of the bbox.
if (control.withConnection && control.getVisibility(fabricObject, key)) {
// reset movement for each control
shouldStroke = true;
ctx.moveTo(control.x * width, control.y * height);
ctx.lineTo(
control.x * width + control.offsetX,
control.y * height + control.offsetY
);
}
});
if (shouldStroke) {
ctx.stroke();
}
}
ctx.restore();
return this;
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.4.0/fabric.min.js"></script>
<canvas id="canvas" height="300" width="400"></canvas>

How to make a small triangle in a rectangle shape in canvas?

I want to add a small triangle similar to this example:
As you can see from the image there is a triangle in the middle of the side, I would like to have that in canvas.
This is what I have so far
// JavaScript for drawing on canvas
// applying colors + three triangles
function draw() {
// canvas with id="myCanvas"
var canvas = document.getElementById('myCanvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
roundRect(ctx, 100, 100, 180, 60, 10, true);
}
}
function roundRect(ctx, x, y, width, height, radius, fill, stroke) {
if (typeof stroke == "undefined") {
stroke = true;
}
if (typeof radius === "undefined") {
radius = 5;
}
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
if (stroke) {
ctx.stroke();
}
if (fill) {
ctx.fill();
}
}
draw();
<canvas id="myCanvas" width="700" height="410">
<p>Some default content can appear here.</p>
</canvas>
<p>Triangles!</p>
4 arcs for a rounded box plus a triangle to fit..
You can create a rounded box with just the rounded corners. Adding the triangle is just a matter of setting out the 3 points between drawing two corners.
The example draws two types of quote boxes, left and right.
The size, corner radius, triangle size and position are all set as arguments.
The animation is just to show the variations possible.
const ctx = canvas.getContext("2d");
// r is radius of corners in pixels
// quoteSize in pixels
// quotePos as fraction of avalible space 0-1
function roundedQuoteBoxLeft(x, y, w, h, r, quoteSize, quotePos) {
// draw 4 corners of box from top, left, top right , bottom right, bottom left
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
// make sure trianle fits
if (quoteSize > h - r * 2) { quoteSize = h - r * 2 }
// get triangle position
var qy = (h - (r * 2 + quoteSize)) * quotePos + r + y;
// draw triangle
ctx.lineTo(x, qy + quoteSize);
ctx.lineTo(x - quoteSize, qy + quoteSize / 2);
ctx.lineTo(x, qy);
// and add the last line back to start
ctx.closePath();
}
function roundedQuoteBoxRight(x, y, w, h, r, quoteSize, quotePos) {
// draw top arcs from left to right
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
// make sure trianle fits
if (quoteSize > h - r * 2) { quoteSize = h - r * 2 }
// get pos of triangle
var qy = (h - (r * 2 + quoteSize)) * quotePos + r + y;
// draw triangle
ctx.lineTo(x + w, qy);
ctx.lineTo(x + w + quoteSize, qy + quoteSize / 2);
ctx.lineTo(x + w, qy + quoteSize);
// draw remaining arcs
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
// and add the last line back to start
ctx.closePath();
}
function sin(time) {
return Math.sin(time) * 0.5 + 0.5;
}
requestAnimationFrame(drawIt)
function drawIt(time) {
ctx.clearRect(0, 0, 500, 250);
// get some sizes
var width = sin(time / 1000) * 100 + 100;
var height = sin(time / 1300) * 50 + 100;
var radius = sin(time / 900) * 20 + 5;
var x = sin(time / 1900) * 20 + 20;
var y = sin(time / 1400) * 50 + 10;
var quotePos = sin(time / 700)
var quoteSize = x - 10;
// set up box render
ctx.lineJoin = "round";
ctx.strokeStyle = "#8D4";
ctx.lineWidth = 4;
ctx.fillStyle = "#482";
// draw left quote box
ctx.beginPath();
roundedQuoteBoxLeft(x, y, width, height, radius, quoteSize, quotePos);
ctx.fill();
ctx.stroke()
x += width + 10;
width = 500 - x - quoteSize;
// draw right quote box
ctx.beginPath();
roundedQuoteBoxRight(x, y, width, height, radius, quoteSize, quotePos);
ctx.fill();
ctx.stroke()
/// animate
requestAnimationFrame(drawIt)
}
<canvas id="canvas" width="500" height="250"></canvas>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Your Canvas</title>
<style type="text/css"><!--
#container { position: relative; }
#imageTemp { position: absolute; top: 1px; left: 1px; }
--></style>
</head>
<body>
<canvas id="imageView" width="600" height="500"></canvas>
<script type="text/javascript">
var canvas, context, canvaso, contexto;
canvaso = document.getElementById('imageView');
context = canvaso.getContext('2d');
context.lineWidth = 5;
context.strokeStyle = '#000000';
context.beginPath();
context.moveTo(143, 224);
context.lineTo(168, 191);
context.stroke();
context.closePath();
context.strokeStyle = '#000000';
context.beginPath();
context.moveTo(143, 221);
context.lineTo(169, 247);
context.stroke();
context.closePath();
context.strokeStyle = '#000000';
context.strokeRect(168, 124, 242, 182);
</script>
</body>
</html>

Javascript draw speech bubble on canvas fillStyle is only changing textcolor

I'm currently trying to create a speechbubble which should be drawn on a canvas element using javascript.
My problem: ctx.fillStyle = "red"; is only changing the ctx.fillText color. But not the background color of the whole path. How could I add a background color to the whole speechbubble?
Jsfiddle: https://jsfiddle.net/dr7q5yay/8/
My current code looks like the following:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
ctx.font = "15px Helvetica";
var text = 'hello test test';
function drawBubble(ctx, x, y, w, h, radius, text)
{
var r = x + w;
var b = y + h;
ctx.beginPath();
ctx.fillStyle = "red";
ctx.fill();
ctx.strokeStyle = "black";
ctx.lineWidth = "2";
ctx.moveTo(x + radius, y);
ctx.lineTo(r - radius, y);
ctx.quadraticCurveTo(r, y, r, y + radius);
ctx.lineTo(r, y + h-radius);
ctx.quadraticCurveTo(r, b, r - radius, b);
ctx.lineTo(x + radius, b);
ctx.quadraticCurveTo(x, b, x, b - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.stroke();
ctx.fillText(text, x + 20, y + 30);
}
drawBubble(ctx, 10, 60, ctx.measureText(text).width + 40, 50, 20, text);
The code is almost there, just do a couple of changes in regards to fill() and fillStyle:
Modified fiddle
You can btw use arc()s to draw a rounded rectangles as well (slightly less overhead).

how to fill certain percentage area of circle in color in html canvas?

I have some 9 circles in my html page.Each circle has to be filled with certain color with certain percentage.I have drawn the circles using html5 canvas element.But i was only able to fill the enitre circle with a color and not certain percantage area.How can i achieve that?
"Fuel tank", filling of circle
Use composite mode:
Use the radius x2 for height (or width)
Draw and fill the complete circle
Use composite mode destination-out
Draw a filled rectangle on top representing the % of the height
The main code would be:
var o = radius * 2, // diameter => width and height of rect
h = o - (o * percent / 100); // height based on percentage
ctx.beginPath();
ctx.arc(x, y, radius, 0, 6.28);
ctx.fill();
ctx.globalCompositeOperation = "destination-out";
ctx.fillRect(x - radius, y - radius, o, h); // this will remove a part of the top
Demo
var ctx = document.querySelector("canvas").getContext("2d"),
pst = 0, dlt = 2;
ctx.fillStyle = "#28f";
function drawCircle(ctx, x, y, radius, percent) {
var o = radius * 2,
h = o - (o * percent / 100);
ctx.globalCompositeOperation = "source-over"; // make sure we have default mode
ctx.beginPath(); // fill an arc
ctx.arc(x, y, radius, 0, 6.28);
ctx.fill();
ctx.globalCompositeOperation = "destination-out"; // mode to use for next op.
ctx.fillRect(x - radius, y - radius, o, h); // "clear" part of arc
ctx.globalCompositeOperation = "source-over"; // be polite, set default mode back
}
(function loop() {
ctx.clearRect(0,0,300,150);
drawCircle(ctx, 70, 70, 60, pst);
pst += dlt;
if (pst <= 0 || pst >= 100) dlt = -dlt;
requestAnimationFrame(loop);
})();
<canvas></canvas>
Pie type
Move to center of circle
Add arc, this will create a line from center to start of arc
Close path, this will create a line from end of arc back to center, and fill
(tip: closePath() is really not necessary with fill() as fill() will close the path implicit, but it's needed if you want to do a stroke() instead).
The essential part being:
ctx.beginPath();
ctx.moveTo(x, y);
ctx.arc(x, y, radius, 0, 2 * Math.PI * percent / 100);
//ctx.closePath(); // for stroke, not needed for fill
ctx.fill();
Demo
var ctx = document.querySelector("canvas").getContext("2d"),
pst = 0, dlt = 2;
ctx.fillStyle = "#28f";
function drawPie(ctx, x, y, radius, percent) {
ctx.beginPath();
ctx.moveTo(x, y);
ctx.arc(x, y, radius, 0, 2 * Math.PI * percent /100);
ctx.fill();
}
(function loop() {
ctx.clearRect(0,0,300,150); drawPie(ctx, 70, 70, 60, pst);
pst += dlt; if (pst <= 0 || pst >= 100) dlt = -dlt;
requestAnimationFrame(loop);
})();
<canvas></canvas>
Outlined circle:
Almost same as with pie type, but with these changes:
Move to outer edge of arc at angle 0 (or the angle you want to start from)
Add arc to path
Stroke (remember to set lineWidth, see demo below)
Essential part:
ctx.beginPath();
ctx.moveTo(x + radius, y); // cos(0) for x = 1, so just use radius, sin(0) = 0
ctx.arc(x, y, radius, 0, 2 * Math.PI * percent /100);
ctx.stroke();
You can adjust gap point using rotation transform or calculating the actual point using trigonometry.
Demo
var ctx = document.querySelector("canvas").getContext("2d"),
pst = 0, dlt = 2;
ctx.strokeStyle = "#28f";
ctx.lineWidth = 8;
function drawWedge(ctx, x, y, radius, percent) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.arc(x, y, radius, 0, 2 * Math.PI * percent /100);
ctx.stroke();
}
(function loop() {
ctx.clearRect(0,0,300,150); drawWedge(ctx, 70, 70, 60, pst);
pst += dlt; if (pst <= 0 || pst >= 100) dlt = -dlt;
requestAnimationFrame(loop);
})();
<canvas></canvas>
Using different starting point
You can change the starting point for the arc using rotation transform or calculating the point manually using trigonometry.
To calculate these manually you can do (angles in radians):
x = radius * Math.cos(angleInRad); // end point for x
y = radius * Math.sin(angleInRad); // end point for y
Just add the total angle to the start angle to get end point.
360° in radians = 2 x PI, so if you want to use angles in degrees, convert them using:
angleInRad = angleInDeg * Math.PI / 180;
Demo, rotated using transfrom and counter-clock-wise mode
var ctx = document.querySelector("canvas").getContext("2d"),
pst = 0, dlt = 2;
ctx.strokeStyle = "#28f";
ctx.lineWidth = 8;
function drawWedge(ctx, x, y, radius, percent) {
ctx.translate(x, y); // translate to rotating pivot
ctx.rotate(Math.PI * 0.5); // rotate, here 90° deg
ctx.translate(-x, -y); // translate back
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.arc(x, y, radius, 0, 2 * Math.PI * percent /100);
ctx.stroke();
ctx.setTransform(1,0,0,1,0,0); // reset transform
}
(function loop() {
ctx.clearRect(0,0,300,150); drawWedge(ctx, 70, 70, 60, pst);
pst += dlt; if (pst <= 0 || pst >= 100) dlt = -dlt;
requestAnimationFrame(loop);
})();
<canvas></canvas>

Display different values at different angles in a circle using html5 canvas

Using HTML5 Canvas and Javascript I need to display different values (represented by a dot maybe) at different angles inside a circle.
Example data:
val 34% # 0°,
val 54% # 12°,
val 23% # 70°,
and so on...
If I have a canvas 300 x 300px and the center of the circle is located at x: 150px and y: 150px with a radius of 150px, how would I calculate where to set my dot for the value 54% at 12 degrees?
My math is kinda terrible xD
I'd appreciate any kind of help and please ask questions if I do not make myself clear enough.
Thank you for listening and thank you in advance for you deep insights :D
EDIT (to explain in more detail):
Here is an image to illustrate what I am trying to accomplish:
I hope this makes my question a little more understandable.
(As you can see, not the same values as above)
Ty for your patience!
You may use this to convert from polar (radius, angle) coordinates to cartesian ones :
// θ : angle in [0, 2π[
function polarToCartesian(r, θ) {
return {x: r*Math.cos(θ), y: r*Math.sin(θ)};
}
For example, if you want to draw at 12°, you may compute the point like this :
var p = polarToCartesian(150, 12*2*Math.PI/360);
p.x += 150; p.y += 150;
EDIT : my polarToCartesian function takes radians as input, as many function in the Canvas API. If you're more used to degrees, you may need this :
function degreesToRadians(a) {
return Math.PI*a/180;
}
Here you go (demo)
var can = document.getElementById('mycanvas');
var ctx = can.getContext('2d');
var drawAngledLine = function(x, y, length, angle) {
var radians = angle / 180 * Math.PI;
var endX = x + length * Math.cos(radians);
var endY = y - length * Math.sin(radians);
ctx.beginPath();
ctx.moveTo(x, y)
ctx.lineTo(endX, endY);
ctx.closePath();
ctx.stroke();
}
var drawCircle = function(x, y, r) {
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
var drawDot = function(x, y, length, angle, value) {
var radians = angle / 180 * Math.PI;
var endX = x + length*value/100 * Math.cos(radians);
var endY = y - length*value/100 * Math.sin(radians);
drawCircle(endX, endY, 2);
}
var drawText = function(x, y, length, angle, value) {
var radians = angle / 180 * Math.PI;
var endX = x + length*value/100 * Math.cos(radians);
var endY = y - length*value/100 * Math.sin(radians);
console.debug(endX+","+endY);
ctx.fillText(value+"%", endX+15, endY+5);
ctx.stroke();
}
var visualizeData = function(x, y, length, angle, value) {
ctx.strokeStyle = "#999";
ctx.lineWidth = "1";
drawAngledLine(x, y, length, angle);
ctx.fillStyle = "#0a0";
drawDot(x, y, length, angle, value);
ctx.fillStyle = "#666";
ctx.font = "bold 10px Arial";
ctx.textAlign = "center";
drawText(x, y, length, angle, value);
}
ctx.fillStyle = "#FFF0B3";
drawCircle(150, 150, 150);
visualizeData(150, 150, 150, 0, 34);
visualizeData(150, 150, 150, 12, 54);
visualizeData(150, 150, 150, 70, 23)
visualizeData(150, 150, 150, 120, 50);
visualizeData(150, 150, 150, -120, 80);
visualizeData(150, 150, 150, -45, 60);

Categories