Today I started learning how to work with canvas by doing a basic loading circle animation. Everything works perfect in small res (ca. 100×100 px) but when I tried much larger it all went distorted and blurred and it looks really bad. So this is my question: Can I somehow turn on some anti-aliasing on or somehow make it more sharpen? I already found plenty of sharpening algorithms but for images and I’m not sure if it will work with this.
Circle loading example: (code is not perfect or even finished because I’m still stuck on that blur)
JSFiddle
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var int_count = 0;
var circ_angle = 1.5;
var count = 10;
var interval = setInterval(function() {
if (int_count == count) {
clearInterval(interval);
}
ctx.clearRect(0, 0, 1000, 1000);
ctx.beginPath();
ctx.arc(100, 70, 50, 1.5 * Math.PI, -1 * Math.PI, true);
ctx.lineWidth = 1;
ctx.strokeStyle = "#717171";
ctx.stroke();
ctx.beginPath();
//1.5 = 0%; 1 = 1/4; 0.5 = 2/4; 0 = 3/4 -1 = full
ctx.font = "40px sarpanch";
ctx.arc(100, 70, 50, 1.5 * Math.PI, circ_angle * Math.PI, true);
//color
if (int_count >= 5 && int_count < 10) {
ctx.strokeStyle = "#ff8000";
}
else if (int_count >= 9) {
ctx.strokeStyle = "#F00";
}
else {
ctx.strokeStyle = "#3a9fbe";
}
ctx.fillText("" + int_count + "", 88, 83);
ctx.lineWidth = 3;
ctx.stroke();
int_count += 1;
circ_angle += (-0.2);
}, 500);
Add the following attributes :
width="759" height="394"
to your <canvas> instead of specifying them in your css
Never resize your canvas with CSS. Instead, set the canvas size with the width and height attributes:
<canvas id="canvas" width="759" height="394"></canvas>
Remove the CSS rules for the canvas.
Next, you’ll have to scale every part with JavaScript:
Working demo
// …
ctx.arc(150, 150, 100, 1.5*Math.PI,-1*Math.PI,true); // instead of 100, 70, 50
// …
ctx.font = "60px sarpanch"; // instead of 40px
ctx.arc(150, 150, 100, 1.5*Math.PI,circ_angle*Math.PI,true); // instead of 100, 70, 50
// …
ctx.fillText(int_count, 130, 170); // instead of 88, 83
// …
Related
This question has been asked twice without the caveat of "Fully Fade Out"
Fastest way of fading out entire contents of a canvas to transparency, not other color
HTML5: Apply transparency to Canvas after drawing through JavaScript
Both of the accepted answers only partially fade out the contents. They both suggest something like:
// semi functional code, but doesn't fully work
ctx.save();
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "destination-in";
ctx.fillStyle = "rgba(0, 0, 0, 0.9)";
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
ctx.restore();
This leaves residue everywhere, never fully fading out anything. See example below:
let canvas = document.getElementsByTagName('canvas')[0];
let ctx = canvas.getContext('2d');
let rand = n => Math.floor(Math.random() * n);
setInterval(() => {
ctx.beginPath();
ctx.arc(rand(300), rand(120), rand(60), Math.PI * 2, 0);
ctx.fillStyle = `rgba(${rand(256)}, ${rand(256)}, ${rand(256)}, 1)`;
ctx.globalCompositeOperation = 'source-over';
ctx.fill();
}, 1000);
let fadeOut = () => {
let fadeAmount = 0.05;
// Note that the colour here doesn't matter! Only the alpha matters.
// The colour here is red, but you'll see no red appear
ctx.fillStyle = `rgba(255, 0, 0, ${1 - fadeAmount})`;
ctx.globalCompositeOperation = 'destination-in';
ctx.fillRect(0, 0, 300, 120);
requestAnimationFrame(fadeOut);
};
requestAnimationFrame(fadeOut);
canvas { border: 3px solid #808080; background-color: #000000; }
<canvas width="300" height="120"></canvas>
The question is: How can one fully fade out elements on a canvas, all the way to transparent?
EDIT: I'm searching for a performant solution that works for heavily layered (think visualizer) situations.
Here is a small sample using globalAlpha, looks good to me, no residue...
each FadingCircle will have own fade, that will determine how fast it fades and if it goes to 0 or below we do not draw it, seems like an easy solution.
You can add colors, random positions and change it as much as you like to suit your needs.
const canvas = document.getElementsByTagName('canvas')[0];
const ctx = canvas.getContext('2d');
class FadingCircle {
constructor(x, y, radius, fade) {
this.x = x
this.y = y
this.radius = radius
this.fade = fade
this.globalAlpha = 1
}
draw(ctx) {
if (this.globalAlpha > 0) {
ctx.beginPath()
ctx.globalAlpha = this.globalAlpha
ctx.arc(this.x, this.y, this.radius, Math.PI * 2, 0)
ctx.fill()
this.globalAlpha -= this.fade
}
}
}
let sml = new FadingCircle(40, 50, 20, 0.01)
let med = new FadingCircle(140, 50, 30, 0)
let big = new FadingCircle(100, 50, 50, 0.005)
let animation = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
big.draw(ctx)
med.draw(ctx)
sml.draw(ctx)
requestAnimationFrame(animation);
};
requestAnimationFrame(animation);
<canvas width="300" height="120"></canvas>
So what I'm trying to do here is make multiple circles on my canvas and individually change the opacity of each of them. Here's what I tried so far, I know that I'm definitely putting the interval in the wrong place but I'm very confused as to how these canvases work exactly:
function makeCircle(x,y)
{
canvas.beginPath();
canvas.arc(x, y, 20, 0, 2 * Math.PI);
color = generateRandomColor();
canvas.fillStyle = color;
canvas.fill();
canvas.lineWidth = 0;
canvas.strokeStyle = color;
setInterval(function()
{
if(canvas.globalAlpha>=.05)
{
canvas.globalAlpha-=.05;
console.log("here");
}
else
{
canvas.globalAlpha = 0;
console.log(canvas.globalAlpha);
clearInterval();
}
}, 2000)
canvas.stroke();
}
Doing this does not change the opacity of the circles at all as far as I can tell.
Not sure what you are trying to do with that nested setInterval, or why use it at all...
Here is what I would do:
function makeCircle(x, y, color, alpha) {
canvas.beginPath();
canvas.globalAlpha = alpha
canvas.arc(x, y, 20, 0, 2 * Math.PI);
canvas.fillStyle = color;
canvas.fill();
}
var canvas_doc = document.getElementById("canvas");
var canvas = canvas_doc.getContext("2d");
makeCircle(20, 20, "red", 0.5)
makeCircle(30, 30, "blue", 0.5)
makeCircle(50, 50, "green", 0.9)
makeCircle(120, 20, "red", 1)
makeCircle(180, 30, "blue", 1)
makeCircle(150, 50, "green", 1)
<canvas id="canvas"></canvas>
Simple, right?
I just added a few more parameters to the function to pass the color and alpha.
Like that we can draw multiple color circles with ease.
You want to create some sort of animation using the globalAlpha...
We can use setInterval for that, here is an example:
function makeCircle(x, y, color, alpha) {
canvas.beginPath();
canvas.globalAlpha = alpha
canvas.arc(x, y, 20, 0, 2 * Math.PI);
canvas.fillStyle = color;
canvas.fill();
}
var globalAlpha = 0.1
function draw() {
globalAlpha += 0.02
if (globalAlpha > 2)
globalAlpha = 0.1
canvas.clearRect(0,0 , 999, 999)
makeCircle(20, 20, "red", globalAlpha)
makeCircle(30, 30, "blue", globalAlpha)
makeCircle(50, 50, "green", globalAlpha)
makeCircle(120, 20, "red", 1)
makeCircle(130, 30, "blue", 1)
makeCircle(150, 50, "green", 1)
}
var canvas_doc = document.getElementById("canvas");
var canvas = canvas_doc.getContext("2d");
setInterval(draw, 50)
<canvas id="canvas"></canvas>
There are a couple of things:
your canvas variable is not the canvas, it's the context. Rename it to avoid confusion (ctx is generally used).
clearInterval takes a parameter: a reference to the interval to clear. Put it in a variable, and use it.
you're not drawing anything inside your interval. You need to do ctx.fill() inside of it.
even with that change, you would only be drawing over the previously drawn circles (and not see any change). Once you drew something, the canvas is just a set of pixels. So if you want to retrieve part of what was behind the circle (by transparency), you need to clear your canvas and start over on every interval (redraw what was previously there before you added the circle). In the demo below, I save the image before drawing the first circle, and restore it on every iteration, but if other things are going on at the same time in your use case, you may actually want to redraw every element individually.
var canvas = document.querySelector('canvas'),
ctx = canvas.getContext("2d");
function makeCircle(x, y) {
// Save the background
var bg = ctx.getImageData(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(x, y, 20, 0, 2 * Math.PI);
color = '#f00'; // For the demo
ctx.fillStyle = color;
ctx.fill();
var timer = setInterval(function() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Redraw the background
ctx.putImageData(bg, 0, 0);
if (ctx.globalAlpha >= .05) {
ctx.globalAlpha -= .05;
ctx.fill();
} else {
ctx.globalAlpha = 0;
clearInterval(timer);
}
}, 50);
}
ctx.fillRect(50, 50, 30, 100); // For the demo
makeCircle(50, 50);
<canvas></canvas>
How to run the function whose name matches the data string inside THIS clicked link?
// From line 0 to 36 there are uninteresting details which are ment to set up canvas.
// The 2 functions corresponding to my question are on lines 47 and 115.
// First function (line 47) counts up to 94%, and Second Function (line 115) counts up to 54%.
// You need the line --> var sim = setInterval(progressSim, 40); <-- on line 102 to fire the function.
var ctx = document.getElementById('my_canvas').getContext('2d');
var ctx2 = document.getElementById('my_canvas2').getContext('2d');
var al = 0;
var start = 4.72;
var cw = ctx.canvas.width;
var ch = ctx.canvas.height;
var diff;
var greenPart = ctx.createLinearGradient(0, 0, 0, 100);
greenPart.addColorStop(0, '#0f2596');
greenPart.addColorStop(1, '#0ea5e8');
var whitePart = ctx.createLinearGradient(0, 0, 0, 100);
whitePart.addColorStop(0, '#fff');
whitePart.addColorStop(1, '#0e97df');
var width = 3;
var width2 = 1;
ctx2.save();
ctx2.beginPath();
ctx2.rect(-width, -width, 70 + width, 70 + width * 2);
ctx2.clip();
// Then we draw the left half
ctx2.beginPath();
ctx2.arc(35, 35, 45, 0, Math.PI * 4, false);
ctx2.fillStyle = '#fff';
ctx2.fill();
ctx2.restore();
// This is the First Function
function progressSim() {
diff = ((al / 100) * Math.PI * 2 * 10).toFixed(2);
ctx.clearRect(0, 0, cw, ch);
ctx.lineWidth = width;
ctx.fillStyle = '#1c295c';
ctx.textAlign = 'center';
ctx.font = "bold 19px Arial";
ctx.fillText(al + '%', cw * .54, ch * .54 + 2, cw);
// First we make a clipping region for the left half
ctx.save();
ctx.beginPath();
ctx.rect(-width2, -width2, 100, 100 + width2 * 2);
ctx.clip();
ctx.lineWidth = width2;
// Then we draw the left half
ctx.strokeStyle = "#d7ecf6";
ctx.beginPath();
ctx.arc(50, 50, 45, 0, Math.PI * 4, 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 = greenPart;
ctx.beginPath();
ctx.lineCap = 'round';
ctx.arc(50, 50, 45, 4.78, diff / 10 + start, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default
// 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 = whitePart;
ctx.lineCap = 'round';
ctx.beginPath();
ctx.arc(50, 50, 45, start, diff / 10 + start, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default
if (al >= 94) {
clearTimeout(sim);
// Add scripting here that will run when progress completes
}
al++;
}
var sim = setInterval(progressSim, 40);
// This is the Second Function
function progressSim2() {
diff = ((al / 100) * Math.PI * 2 * 10).toFixed(2);
ctx.clearRect(0, 0, cw, ch);
ctx.lineWidth = width;
ctx.fillStyle = '#1c295c';
ctx.textAlign = 'center';
ctx.font = "bold 19px Arial";
ctx.fillText(al + '%', cw * .54, ch * .54 + 2, cw);
// First we make a clipping region for the left half
ctx.save();
ctx.beginPath();
ctx.rect(-width2, -width2, 100, 100 + width2 * 2);
ctx.clip();
ctx.lineWidth = width2;
// Then we draw the left half
ctx.strokeStyle = "#d7ecf6";
ctx.beginPath();
ctx.arc(50, 50, 45, 0, Math.PI * 4, 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 = greenPart;
ctx.beginPath();
ctx.lineCap = 'round';
ctx.arc(50, 50, 45, 4.78, diff / 10 + start, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default
// 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 = whitePart;
ctx.lineCap = 'round';
ctx.beginPath();
ctx.arc(50, 50, 45, start, diff / 10 + start, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default
if (al >= 54) {
clearTimeout(sim);
// Add scripting here that will run when progress completes
}
al++;
}
* {
margin: 0;
padding: 0;
}
a {
display: inline-block;
text-decoration: none;
text-transform: uppercase;
font-size: 20px;
margin: 10px 30px;
color: teal;
}
one
two
<div class="canvaswrap">
<canvas id="my_canvas2" width="70" height="70"></canvas>
<canvas id="my_canvas" width="100" height="100"></canvas>
</div>
When I click the given link (there are 2 links) I want to store its data attribute inside variable and fire the function whose name matches that stored data attribute string.
For example, when I click 2nd link I want to store its data attribute progressSim2 inside variable and fire the function progressSim2().
First function counts up to 94% whereas the Second function counts up to 54%.
Below my attempt ( I am using jQuery):
$(function () {
jQuery('a').click(function(e) {
e.preventDefault();
var storedata = $($.attr(this, 'data'));
jQuery("#canvas1").fadeIn();
var sim = setInterval(storedata , 40);
});
});
The closest way to your question, in my opinion: create a "mapping" object and call upon its properties:
var OBJECT_MAPS = {
'dummy1': function () => {},
'dummy2': function () => {}
}
Use it like this:
some_event_handler(item_name) => {
// Check if function exists in map, and it is actually a function
if (typeof OBJECT_MAPS[item_name] === 'function'){
// A) If execution context doesn't matter
OBJECT_MAPS[item_name](param1, param2, ...);
// B) If execution context matters (change "this" to required context)
OBJECT_MAPS[item_name].call(this, param1, param2, ...)
} else {
console.error('Callback by name ' + item_name + ' does not exist');
}
}
I would discourage using global functions bound to window object as this is generally considered a bad practice. Manipulating and overriding global objects is very easy from browser's console, so anyone would be able to change how your functions work. Always hide your functionality from bad people, even if it is inside javascript :)
Other ways to do this:
Use switch statement to "switch" between function callbacks.
Make your callbacks "event listeners". Emit events when item is chosen.
Also, User by name "Hikarunomemory" noticed incorrect usage of attr function in your code, please see his response.
Hope this helps you.
If you declare you progressSim and progressSim2 methods globally. You can call them like this:
window[$.attr(this, 'data')]()
or you can store link to them in another Object:
object[$.attr(this, 'data')]()
var storedata = $($.attr(this, 'data')); is incorrect.
To get a attribute, you should use $(this).attr('data') or $.attr(this, 'data')
Then, set the functions as a global variable, and use window['funcName']() to execute the function.
Here's a simple demo:
$('a').on('click', function(e) {
e.preventDefault();
var storedata = $(this).attr('data');
console.log(storedata)
window[storedata]();
})
var progressSim = function() {
console.log('execute function1')
}
var progressSim2 = function() {
console.log('execute function2')
}
* {
margin: 0;
padding: 0;
}
a {
display: inline-block;
text-decoration: none;
text-transform: uppercase;
font-size: 20px;
margin: 10px 30px;
color: teal;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
one
two
<div class="canvaswrap">
<canvas id="my_canvas2" width="70" height="70"></canvas>
<canvas id="my_canvas" width="100" height="100"></canvas>
</div>
As a side note,
eval() is a dangerous function, which executes the code it's passed
with the privileges of the caller. If you run eval() with a string
that could be affected by a malicious party, you may end up running
malicious code on the user's machine with the permissions of your
webpage / extension. More importantly, a third-party code can see the
scope in which eval() was invoked, which can lead to possible attacks
in ways to which the similar Function is not susceptible.
From MDN
the function "eval" runs string as a code,
example:
function progressSim(){console.log("sim is ok")}
eval("progressSim()")
how can I fill the "new" canvas circle that appears next to the older one.
There is no problem with rectangle for example:
**
ctx.fillStyle = 'rgba('+quadratto.r+','+quadratto.g+','+quadratto.b+',1)';
quadratto.x += quadratto.speedX;
quadratto.y += quadratto.speedY;
quadratto.speedY += quadratto.speedY*(-0.15);
ctx.fillRect(quadratto.x-quadratto.h/4, quadratto.y-quadratto.h/2, 2, 2);**
What I want to do?
I'm creating animation in canvas where random-sized-color circle will appear and
it will move in a specified direction. The new canvas layaer will appear in the next frame (fps) with a new(old) circle.
var myCanvasPattern = document.createElement('canvas');
myCanvasPattern.width = window.innerWidth;
myCanvasPattern.height = window.innerHeight;
document.body.appendChild(myCanvasPattern);
var ryC = myCanvasPattern.getContext('2d');
function lottery(min, max) {
return Math.floor(Math.random()*(max-min+1))+min;
}
var allQuadro = [];
var fps = 50;
var lastTime = 0;
animationLoop();
function animationLoop(time){
requestAnimationFrame( animationLoop );
if(time-lastTime>=1000/fps){
lastTime = time;
for(var i=0;i<10;i++){
allQuadro.push({
r : lottery(0, 240),
g : lottery(0, 240),
b : lottery(0, 240),
circleR : lottery(10, 30),
x : myCanvasPattern.width/2,
y : myCanvasPattern.height/2,
speedX : lottery(-1000,1000)/100,
speedY : lottery(-1000,1000)/100
})
}
ryC.fillStyle = 'rgba(255,255,255,0.2)';
ryC.fill(0,0,myCanvasPattern.width, myCanvasPattern.height);
for(var i=0; i<allQuadro.length;i++){
var circle = allQuadro[i];
ryC.fillStyle = 'rgba('+circle.r+','+circle.g+','+circle.b+',1)';
circle.x += circle.speedX;
circle.y += circle.speedY;
//HERE's THE PROBLEM BELOW. HOW TO CREATE NEW ONE THAT APPEARS NEXT TO PREVIOUS ONE WITH NEW RANDOM COLOR
ryC.arc(circle.x-circle.circleR/2, circle.y-circle.circleR/2, circleR, 0, 2 * Math.PI);
//ryC.fill();
}
// ryC.fillStyle = 'rgba('+r+','+g+','+b+',1)';
//ryC.arc(x+speedX, y+speedY, circleR, 0, 2 * Math.PI);
//ryC.fill();
}
}
body {
margin: 0;
padding: 0;
overflow: hidden;
}
The fillRect() will fill directly to the canvas without going via a path (versus for example rect()).
The arc() on the other hand will add to a path which needs to be filled later. It also require the path to be cleared in-between the calls using beginPath().
A simple way to think about it is to wrap the necessary code into a function that acts like fillRect():
function fillArc() {
ctx.beginPath(); // clear current path
ctx.arc.apply(ctx, arguments); // apply arguments to arc() call
ctx.fill();
// notice that the arc still exist on the path after this call
// so to "truly" behave like fillRect() you could consider an
// additional beginPath() here.. it will depend on your code
}
In action:
var ctx = c.getContext("2d");
ctx.fillStyle = "#09f";
fillArc(70, 70, 70, 0, 6.28);
ctx.fillStyle = "#0a9";
fillArc(220, 70, 70, 0, 6.28);
function fillArc() {
ctx.beginPath();
ctx.arc.apply(ctx, arguments);
ctx.fill();
}
<canvas id=c></canvas>
If you are bold you can also add the method to the context itself before calling getContext():
CanvasRenderingContext2D.prototype.fillArc = function() {
this.beginPath();
this.arc.apply(this, arguments);
this.fill();
}
The use it like any other method:
ctx.fillArc( ... );
CanvasRenderingContext2D.prototype.fillArc = function() {
this.beginPath();
this.arc.apply(this, arguments);
this.fill();
}
var ctx = c.getContext("2d");
ctx.fillStyle = "#09f";
ctx.fillArc(70, 70, 70, 0, 6.28);
ctx.fillStyle = "#0a9";
ctx.fillArc(220, 70, 70, 0, 6.28);
<canvas id=c></canvas>
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Growing Circles</title>
</head>
<body>
<canvas id="c" width="960" height="720"></canvas>
</body>
</html>
JavaScript
var canvas = document.getElementById( "c" ),
ctx = canvas.getContext( "2d" );
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc( 500, 350, 60, 0, 2 * Math.PI, false );
ctx.fillStyle = "#4DA54D";
ctx.fill();
ctx.strokeStyle = "DarkRed";
ctx.stroke();
ctx.beginPath();
ctx.arc( 500, 350, 120, 0, 2 * Math.PI, false );
ctx.strokeStyle = "OliveDrab";
ctx.stroke();
ctx.beginPath();
ctx.arc( 500, 350, 180, 0, 2 * Math.PI, false );
ctx.strokeStyle = "#530053";
ctx.stroke();
ctx.beginPath();
ctx.arc( 500, 350, 240, 0, 2 * Math.PI, false );
ctx.strokeStyle = "#208181";
ctx.stroke();
ctx.beginPath();
ctx.arc( 500, 350, 300, 0, 2 * Math.PI, false );
ctx.strokeStyle = "#CC7A00";
ctx.stroke();
ctx.beginPath();
ctx.arc( 500, 350, 360, 0, 2 * Math.PI, false );
ctx.strokeStyle = "#205450";
ctx.stroke();
I want to divide This demo into 24 hours timeline by using lines. I tried few but not up-to mark.It is going very big in code!
Is there any other way to solve this in small piece of code like using slice as in Tried demo ?
Tried Demo
My Requirement is to do as This demo.
I want to have This demo to be sliced 24/7 where 24 represent hours and 7 for days.
Further Requirement :: Even i must be able to access the particular arc which i want depending on the day and hour !
Finally i want to have a look like this Image I will pass the arguments(Day, hour,Color) and then that particular segment should change the color, as i shown in the Image.
This is how i tried to print the numbers ..
function drawnumbers(){
for(var i=1; i< hours+1;i++){
context.font="18px sans-serif";
context.fillText(+i,20+i*30,20+i*30);
}
}but i want them on the outer circle as in png Image
Although Harsh has already provided a very useful answer, it relates to the wireframe drawing which you depicted.
I thought it would also be useful to show you how you could achieve the drawing of the individual segments.
I think you have asked for a little too much in your PNG as we would practically be creating your project for you, but with my answer and Harsh's answer I believe you can get what you want:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
// centre or center for US :) the drawing
var x = canvas.width / 2;
var y = canvas.height / 2;
// number of days
var days = 7;
// number of hours
var hours = 24;
// one segment represents an hour so divide degrees by hours
var segmentWidth = 360 / hours;
// begin at 0 and end at one segment width
var startAngle = 0;
var endAngle = segmentWidth;
// how thick you want a segment
var segmentDepth = 30;
function init() {
for (var i = 1; i <= days; i++) {
drawSegments(i * segmentDepth);
}
}
function drawSegments(radius) {
for (var i = 0; i < hours; i++) {
context.beginPath();
context.arc(x, y, radius,
(startAngle * Math.PI / 180), (endAngle * Math.PI / 180), false);
context.lineWidth = segmentDepth;
context.strokeStyle = '#' +
(Math.random() * 0xFFFFFF << 0).toString(16);
context.stroke();
// increase per segment
startAngle += segmentWidth;
endAngle += segmentWidth;
}
}
// start drawing our chart
init();
See my http://jsfiddle.net/U2tPJ/6/ demo.
Man, you are doing PROGRAMMING, so why are you afraid of using the tools? Use loops and variables.
Dial
var canvas = document.getElementById( "c" ),
ctx = canvas.getContext( "2d" ),
strokes = ["DarkRed", "OliveDrab", "#530053", "#208181", "#CC7A00", "#205450"];
ctx.lineWidth = 3;
for(var i=0; i<7; i++) {
ctx.beginPath();
ctx.arc( 500, 350, 420-60*i, 0, 2 * Math.PI, false );
if(i==6) {
ctx.fillStyle = "#4DA54D";
ctx.fill();
}
ctx.strokeStyle = strokes[i];
ctx.stroke();
}
// Now do the same for the 24 spokes (Use mathematical formulae)
// this is just from the top of my head, may need some adjustment
angle = 360/25; // you have 24 spokes, so 25 sectors (first and last one overlap or are the same)
for(var j=0; j<24; j++) {
ctx.beginPath();
ctx.moveTo(500, 350);
ctx.lineTo(500+420*Math.sin(angle*j), 350-420*Math.cos(angle*j));
ctx.strokeStyle = "blue";
ctx.stroke();
}
Labels
For printing labels on the dial:
angle = 360/23;
for(var i=0; i<23; i++) {
x = <centerX> + <radius> * Math.sin(angle*i*Math.PI/180) // convert from radian to angle
y = <centerY> - <radius> * Math.cos(angle*i*Math.PI/180)
context.fillText(i+1, x, y);
}