canvas in HTML5 and creating multiple circles - html

following the w3c school explanation of canvas, I understand creating shapes...
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 20;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
</script>
This will make a green circle where my canvas is.
<canvas id="myCanvas"></canvas>
However - I want to apply that "circle" to multiple places on the page, and doing this by ID would be ridiculous.
How does one apply a context (as defined above) to multiple canvas'?? I' assume using class, this seems the logical way.
<canvas class="redDot"></canvas>
<canvas class="redDot"></canvas>
<canvas class="redDot"></canvas>
<canvas class="greenDot"></canvas>
<canvas class="greenDot"></canvas>
<canvas class="blueDot"></canvas>
<canvas class="blueDot"></canvas>
<canvas class="blueDot"></canvas>

You would have to iterate through each Element to apply the changes. I.E:
var dots = document.getElementsByClassName('dots');
for (var i=0;i<dots.length;i++){
var canvas = dots[i];
var context = canvas.getContext('2d');
// Draw circles here
}
Ideally, you would only have one canvas element which you can draw multiple circles
var canvas = document.getElementById('myOneAndOnlyCanvas');
// Using CSS and layering can make background
var context = canvas.getContext('2d');
dot('red',50,50);
dot('blue',100,50);
//etc..
function dot(color,x,y){
context.beginPath();
context.arc(y, y, radius, 0, 2 * Math.PI, false);
context.fillStyle = color;
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
}
But if that doesn't fit your use case what about using an SVG object?
Since you mentioned w3 schools: http://www.w3schools.com/svg/
But ideally check out: http://www.w3.org/Graphics/SVG/
Mind, Dom Heavy pages can seriously hurt load times. Depending on what you want to do it might be wiser just to use an image (eg. a large cluster of dots).

Related

HTML5 Canvas: How to create multiple shapes in one canvas and position/style them independently

I'm new to using HTML canvas and am hoping to be able to create a button that can be transformed and interacted with using the canvas. But I'm struggling to draw the multiple component parts and style/adjust them independently.
For basics, I'm looking for a grey circle with a heart outline in the centre, almost filling the grey circle. The circle will be drawn using .arc and the heart outline is an svg path.
1) How do I have multiple shapes on the canvas that I can manipulate independently from one another? I started by creating multiple contexts of the same canvas, is this right? E.g circleContext and heartContext so that I could set different fill colours and such only to specific contexts. Is this the correct approach?
2) How do I position these shapes on the canvas? I have set up canvasCentreX and canvasCentreY with the hope of using these co-ordinates to set shapes where I want them but I cannot seem to position the path2D correctly using .moveTo or any of the other available methods.
Here is what I have:
https://codepen.io/anon/pen/xMZxvq
// Canvas setup
const canvas = document.getElementById('saveButtonCanvas');
const ctx = canvas.getContext('2d');
const canvasCentreX = canvas.width / 2;
const canvasCentreY = canvas.height / 2;
// Draw grey background circle
const circleContext = canvas.getContext('2d');
const backgroundCircleRadius = canvas.width / 2;
circleContext.beginPath();
circleContext.arc(canvasCentreX, canvasCentreY, backgroundCircleRadius, 0, 2 * Math.PI);
circleContext.fillStyle = '#eee';
circleContext.fill();
// Draw outline of heart
const heartContext = canvas.getContext('2d');
heartContext.lineWidth = 2;
const heartOutline = new Path2D("M12.7047547,3.56374623e-06 C11.2594659,0.00100271425 9.94933816,0.599893524 8.99600456,1.56687138 C8.04033388,0.603290635 6.72923238,0.00779693764 5.28238554,0.00959540854 C2.36181543,0.0127926901 -0.00252980442,2.44652348 2.03149576e-06,5.44317565 C0.00837656568,12.3289212 9.01294838,16 9.01294838,16 C9.01294838,16 18.0097299,12.3067401 17.9999921,5.41919604 C17.9962917,2.42234403 15.6261038,-0.00339354795 12.7047547,3.56374623e-06 L12.7047547,3.56374623e-06 Z");
heartOutline.moveTo(canvasCentreX, canvasCentreY);
heartContext.stroke(heartOutline);
#saveButtonCanvas {
height: 80px;
width: 80px;
margin:30px;
cursor: pointer;
}
<canvas id="saveButtonCanvas" width="80" height="80"></canvas>
This is wow I would do it: first I need the size of the svg path. In order to get the size you can draw the svg path inside an svg element and use the getBBox() method. This method is returning an object with the size and the coords of the bounding box. I use the width and the height.
You need to get the context only once.
To move the heart in the center of your canvas I would use translate
// Canvas setup
const canvas = document.getElementById('saveButtonCanvas');
const ctx = canvas.getContext('2d');
const canvasCentreX = canvas.width / 2;
const canvasCentreY = canvas.height / 2;
// Draw grey background circle
//const circleContext = canvas.getContext('2d');
const backgroundCircleRadius = canvas.width / 2;
ctx.beginPath();
ctx.arc(canvasCentreX, canvasCentreY, backgroundCircleRadius, 0, 2 * Math.PI);
ctx.fillStyle = '#eee';
ctx.fill();
// Draw outline of heart
//const heartContext = canvas.getContext('2d');
ctx.lineWidth = 2;
const hw = 18;// width of the heart
const hh = 16;// height of the heart
ctx.save();
// translate the heart in the center of the canvas
ctx.translate(-hw/2, -hh/2);
ctx.translate(canvasCentreX,canvasCentreY);
const heartOutline = new Path2D("M12.7047547,3.56374623e-06 C11.2594659,0.00100271425 9.94933816,0.599893524 8.99600456,1.56687138 C8.04033388,0.603290635 6.72923238,0.00779693764 5.28238554,0.00959540854 C2.36181543,0.0127926901 -0.00252980442,2.44652348 2.03149576e-06,5.44317565 C0.00837656568,12.3289212 9.01294838,16 9.01294838,16 C9.01294838,16 18.0097299,12.3067401 17.9999921,5.41919604 C17.9962917,2.42234403 15.6261038,-0.00339354795 12.7047547,3.56374623e-06 L12.7047547,3.56374623e-06 Z");
ctx.stroke(heartOutline);
ctx.restore();
#saveButtonCanvas {
height: 80px;
width: 80px;
margin:30px;
cursor: pointer;
}
canvas{border:1px solid}
<canvas id="saveButtonCanvas" width="80" height="80"></canvas>
In the case you need to draw a bigger heart I would use scale:
// Canvas setup
const canvas = document.getElementById('saveButtonCanvas');
const ctx = canvas.getContext('2d');
const canvasCentreX = canvas.width / 2;
const canvasCentreY = canvas.height / 2;
// Draw grey background circle
//const circleContext = canvas.getContext('2d');
const backgroundCircleRadius = canvas.width / 2;
ctx.beginPath();
ctx.arc(canvasCentreX, canvasCentreY, backgroundCircleRadius, 0, 2 * Math.PI);
ctx.fillStyle = '#eee';
ctx.fill();
// Draw outline of heart
//const heartContext = canvas.getContext('2d');
ctx.lineWidth = 1;
const hw = 18;// the width of the heart
const hh = 16;// the height of the heart
let scale = 3;// the scale for the heart
ctx.save();
ctx.translate(-scale*hw/2, -scale*hh/2);
ctx.translate(canvasCentreX,canvasCentreY);
ctx.scale(scale,scale)
const heartOutline = new Path2D("M12.7047547,3.56374623e-06 C11.2594659,0.00100271425 9.94933816,0.599893524 8.99600456,1.56687138 C8.04033388,0.603290635 6.72923238,0.00779693764 5.28238554,0.00959540854 C2.36181543,0.0127926901 -0.00252980442,2.44652348 2.03149576e-06,5.44317565 C0.00837656568,12.3289212 9.01294838,16 9.01294838,16 C9.01294838,16 18.0097299,12.3067401 17.9999921,5.41919604 C17.9962917,2.42234403 15.6261038,-0.00339354795 12.7047547,3.56374623e-06 L12.7047547,3.56374623e-06 Z");
ctx.stroke(heartOutline);
ctx.restore();
#saveButtonCanvas {
height: 80px;
width: 80px;
margin:30px;
cursor: pointer;
}
canvas{border:1px solid}
<canvas id="saveButtonCanvas" width="80" height="80"></canvas>
I hope this helps.

In canvas, how to draw 2 semi-transparent overlapping circles

I want to draw two circles on the gray rectangle in HTML canvas.
I attempted the following steps:
Fill rectangle gray
Change globalComposition lighter to mix two colors.
I want to mix only blue and red, not gray and gray rectangle.
You can easily achieve this property by fill style of the circles in rgba, where the last parameter will be alpha or opacity. The syntax will be something like this
circle.fillStyle = "rgba(255, 255, 255, 0.5)";
//can be used to fill as red object with opacity of 0.5
The complete code to achieve the desired effect will be something like this.
// JavaScript Code
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerleftX = canvas.width / 4;
var centerRightX = 3 * canvas.width / 4;
var centerY = canvas.height / 2;
var radius = 70;
context.beginPath();
context.rect(0, 0, 400, 200);
context.fillStyle = 'rgb(200,200,200)';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
context.beginPath();
context.arc(centerleftX + 50, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = "rgba(0, 0,255,0.7)";
context.fill();
context.beginPath();
context.arc(centerRightX - 50, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = "rgba(255, 0,0,0.7)";
context.fill();
<!-- HTML Code -->
<canvas id="myCanvas" width="400" height="200">

Animate a Fill Circle using Canvas

Basically I want to be able to Fill a Circle using canvas, but it animate to a certain percentage.
I.e only have the circle fill up 80% of the way.
My canvas knowledge isn't amazing, Here is an image i made in photoshop to display what i want.
I want the circle to start empty and then Fill up to say 70% of the circle.
Is this possible with Canvas, if so? can anyone shed some light on how to do it?
Here is a fiddle of what I've managed
http://jsfiddle.net/6Vm67/
var canvas = document.getElementById('Circle');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 80;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = '#13a8a4';
context.fill();
context.lineWidth = 10;
context.strokeStyle = '#ffffff';
context.stroke();
Any help would be massively appreciated
Clipping regions make this very easy. All you have to do is make a circular clipping region and then fill a rectangle of some size to get a "partial circle" worth of fill. Here's an example:
var canvas = document.getElementById('Circle');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 80;
var full = radius*2;
var amount = 0;
var amountToIncrease = 10;
function draw() {
context.save();
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.clip(); // Make a clipping region out of this path
// instead of filling the arc, we fill a variable-sized rectangle
// that is clipped to the arc
context.fillStyle = '#13a8a4';
// We want the rectangle to get progressively taller starting from the bottom
// There are two ways to do this:
// 1. Change the Y value and height every time
// 2. Using a negative height
// I'm lazy, so we're going with 2
context.fillRect(centerX - radius, centerY + radius, radius * 2, -amount);
context.restore(); // reset clipping region
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.lineWidth = 10;
context.strokeStyle = '#000000';
context.stroke();
// Every time, raise amount by some value:
amount += amountToIncrease;
if (amount > full) amount = 0; // restart
}
draw();
// Every second we'll fill more;
setInterval(draw, 1000);
http://jsfiddle.net/simonsarris/pby9r/
This is a little more dynamic, object-oriented version, so you can configure the options as the circle radius, border width, colors, duration and step of animation, you can also animate the circle to a certain percentage. It was quite fun to write this.
<canvas id="Circle" width="300" height="300"></canvas>
<script>
function Animation( opt ) {
var context = opt.canvas.getContext("2d");
var handle = 0;
var current = 0;
var percent = 0;
this.start = function( percentage ) {
percent = percentage;
// start the interval
handle = setInterval( draw, opt.interval );
}
// fill the background color
context.fillStyle = opt.backcolor;
context.fillRect( 0, 0, opt.width, opt.height );
// draw a circle
context.arc( opt.width / 2, opt.height / 2, opt.radius, 0, 2 * Math.PI, false );
context.lineWidth = opt.linewidth;
context.strokeStyle = opt.circlecolor;
context.stroke();
function draw() {
// make a circular clipping region
context.beginPath();
context.arc( opt.width / 2, opt.height / 2, opt.radius-(opt.linewidth/2), 0, 2 * Math.PI, false );
context.clip();
// draw the current rectangle
var height = ((100-current)*opt.radius*2)/100 + (opt.height-(opt.radius*2))/2;
context.fillStyle = opt.fillcolor;
context.fillRect( 0, height, opt.width, opt.radius*2 );
// clear the interval when the animation is over
if ( current < percent ) current+=opt.step;
else clearInterval(handle);
}
}
// create the new object, add options, and start the animation with desired percentage
var canvas = document.getElementById("Circle");
new Animation({
'canvas': canvas,
'width': canvas.width,
'height': canvas.height,
'radius': 100,
'linewidth': 10,
'interval': 20,
'step': 1,
'backcolor': '#666',
'circlecolor': '#fff',
'fillcolor': '#339999'
}).start( 70 );
</script>

Circle Animation

Can someone help me a little bit with thath canvas, i am learning it and cant manage to make a circle which when r comes to 100 it goes back to 0 animation. So its some kind of a zooming image.
I draw a circle like this:
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 70;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
</script>
Now how i can animate this now with canvas thath when it radius reaches 100 it goes instantly back to 0 and then it goes again to 100.
Thanks
Look at math sinus function http://www.digitalmedia.cz/shared/clanky/438/graf.gif
Lets take advantage on that its value is going to 1 and then goes back to 0 for angles between 0 and PI
var period = 500; // [miliseconds]
var linearMod = Date.now() % period / period; // this goes from 0 to 1
var mod = Math.sin(linearMod * Math.PI); // and here with simple easing we create
// bouncing
var r = someRadius * mod; // voila
With this approach you are additionally gaining simple sinusoidal easing which feels much more dynamic.
Here is a little fiddle for you http://jsfiddle.net/rezoner/6acF9/
You dont have to base linearMod upon time - you can assign it to a slider control or whatever you wish.

Canvas items not rendering properly

I have defined the two functions to render a circle and a triangle. Very straight forward stuff.
function circle(offset, size){
var color = $("#color option:selected").val();
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
radius = size * 1;
context.beginPath();
context.arc(offset, 2, radius, 0, 2 * Math.PI, false);
context.fillStyle = color;
context.fill();
}
function triangle(offset, size){
var color = $("#color option:selected").val();
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = size * 6;
var height = size * 5;
var padding = 0;
// Draw a path
context.beginPath();
context.moveTo(offset + width/2, padding);
context.lineTo(offset + width, height + padding);
context.lineTo(offset, height + padding);
context.closePath();
// Fill the path
context.fillStyle = color;
context.fill();
}
I am have added the canvas to my page with:
<canvas id="canvas"></canvas>
For some reason I can see the circle and square a not rendering correctly. See attached screen shots.
I can almost guarantee that it is because you are setting the width and height of the Canvas using CSS width and height and not the <canvas> html attributes.
You need to define the width/height either in the canvas tag:<canvas width="500" height="500">
or in code:
var canvas = document.getElementById("canvas");
canvas.width = 500;
canvas.height = 500;
And not by CSS. If you did this:
<canvas style="width: 500px; height: 500px;">
Then you would have a 300x150 canvas (the default size) that was scaled/warped to be 500x500, which is almost certainly what you're getting.
(I wrote the above freehand so there might be a typo, but you get the idea)