Well I've got a few question to ask! Firstly What this code is doing is creating and drawing snowflakes with unique density which will all fall at a different rate. My first question is how do i make this loop continuous?
Secondly, I've translated my origin point(0,0) to the middle of the canvas (it was part of the criteria). I've now got this issue in which that when the snowfall is called it will either be drawn on the left side of the screen or the right, not both. How do i solve this?
Finally i know when doing animations that you have to clear the canvas after each re-drawing, however i haven't added this in and yet it still works fine?
//Check to see if the browser supports
//the addEventListener function
if(window.addEventListener)
{
window.addEventListener
(
'load', //this is the load event
onLoad, //this is the evemnt handler we going to write
false //useCapture boolen value
);
}
//the window load event handler
function onLoad(Xi, Yy) {
var canvas, context,treeObj, H, W, mp;
Xi = 0;
Yy = 0;
mp = 100;
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
W = window.innerWidth;
H = window.innerHeight;
canvas.width = W;
canvas.height = H;
context.translate(W/2, H/2);
var particles = [];
for(var i = 0; i < mp; i++) {
particles.push({
x: Math.random()*-W, //x
y: Math.random()*-H, //y
r: Math.random()*6+2, //radius
d: Math.random()* mp // density
})
}
treeObj = new Array();
var tree = new TTree(Xi, Yy);
treeObj.push(tree);
function drawCenterPot(){
context.beginPath();
context.lineWidth = "1";
context.strokeStyle = "Red";
context.moveTo(0,0);
context.lineTo(0,-H);
context.lineTo(0, H);
context.lineTo(-W, 0);
context.lineTo(W,0);
context.stroke();
context.closePath();
}
function drawMountain() {
context.beginPath();
context.fillStyle = "#FFFAF0";
context.lineWidth = "10";
context.strokeStyle = "Black";
context.moveTo(H,W);
context.bezierCurveTo(-H*10,W,H,W,H,W);
context.stroke();
context.fill();
}
function drawSky() {
var linearGrad = context.createLinearGradient(-100,-300, W/2,H);
linearGrad.addColorStop(0, "#000000");
linearGrad.addColorStop(1, "#004CB3");
context.beginPath();
context.fillStyle = linearGrad;
context.fillRect(-W/2, -H/2, W, H);
context.stroke();
context.fill();
drawMountain();
drawCenterPot();
}
function drawSnow(){
context.fillStyle = "White";
context.beginPath();
for(i = 0; i<mp; i++)
{
var p = particles[i];
context.moveTo(p.x,p.y);
context.arc(p.x, p.y, p.r, Math.PI*2, false);
}
context.fill();
}
function update() {
var angle = 0;
angle+=0.1;
for(var i=0; i<mp; i++) {
var p = particles[i];
p.x += Math.sin(angle) * 2;
p.y += Math.cos(angle+p.d) + 1 * p.r;
}
drawSky();
drawSnow();
draw();
}
function draw() {
for(var i =0; i < treeObj.length; i++)
{
context.save();
context.translate(Xi-H,Yy-W);
context.scale(1, 1);
treeObj[0].draw(context);
context.restore();
}
}
setInterval(update, 33);
}
About your animation:
What's happening is your flakes are falling out of view below the bottom of the canvas.
So when any flake's p.y+p.r > canvas.height you could:
destroy that flake and (optionally) add another falling from above the canvas
or
"recycle" that flake by changing its p.y to above the canvas.
About your design working without context.clearRect:
In your design, when you fill the whole canvas with "sky", you are effectively clearing the canvas.
About your flakes only falling on half the screen:
Instead of translating to mid-screen:
Don't translate at all and let p.x be any Math.random()*canvas.width
Related
I want to be able to hover my mouse over different rectangles and have the rectangle change color when hovered, what I have now works for the last rectangle but the others get cleared. The rectangles are created using a class/constructor, an array, and a loop. Code is below:
/*Variables*/
let canvas = document.querySelector('#canvas'),
ctx = canvas.getContext('2d'),
square;
/*Board Class*/
class Board {
constructor(startX, startY, height, width, angle) {
this.startX = startX;
this.startY = startY;
this.height = height;
this.width = width;
this.angle = angle;
}
drawBoard() {
let canvasWidth = window.innerWidth * .95,
drawWidth = canvasWidth * this.width,
drawHeight = canvasWidth * this.height,
drawStartX = canvasWidth * this.startX,
drawStartY = canvasWidth * this.startY;
square = new Path2D();
ctx.rotate(this.angle * Math.PI / 180);
square.rect(drawStartX, drawStartY, drawHeight, drawWidth);
ctx.fillStyle = 'red';
ctx.fill(square);
}
}
/*Event Listener for changing rectangle color and redrawing*/
canvas.addEventListener('mousemove', function(event) {
if (ctx.isPointInPath(square, event.offsetX, event.offsetY)) {
ctx.fillStyle = 'white';
}
else {
ctx.fillStyle = 'red';
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fill(square);
});
/*Instantiate Array*/
let b = [];
/*Loop to create boards and push to array*/
for(let i = 1; i < 11; i++){
b.push(new Board(.05 * i, .25, .04, .03, 0));
}
/*Function to loop through array and draw boards when page loads*/
function loadFunctions(){
background.draw();
b.forEach(function(board){
board.drawBoard();
})
}
This is my first project with the Canvas API and it's giving me a lot of trouble, normally I could identify the shape by class/id if it where made with a regular HTML element but I'm not sure where to go from here...
I've tried looping through the array that contains the board info but cannot get anything to work. Any help is appreciated!
Thanks
Let's step through your code, to get a better picture of what's going on.
As soon as you move your mouse over the canvas, the mousemove listener gets fired and executes it's associated callback function.
Inside this callback function we'll find this as the very first line:
if (ctx.isPointInPath(square, event.offsetX, event.offsetY))
So this if-statement checks it the current mouse position is inside of square. Well, the big question is: what is square actually?
If we look over your code a bit more, we'll find out that it's a global variable, which gets some value inside the Board class drawBoard() function as:
square = new Path2D();
square.rect(drawStartX, drawStartY, drawHeight, drawWidth);
Apparently it's a Path2D holding the rectangle of one of the bars - but which one actually?
Let's take a look at this function:
for (let i = 0; i < 10; i++) {
b.push(new Board(0.05 * i, 0.25, 0.04, 0.03, 0));
}
and
function loadFunctions() {
b.forEach(function(board) {
board.drawBoard();
})
}
In the first loop, you're populating the array b with ten instances of Board and in the forEach loop, you're calling each Board's drawBoard() function.
What does all this mean? Yes, square will always hold a reference to the bar, which's drawBoard() function has been called the last time - which will always be the last Board in your array.
To summarize: the only bar your checking in the mousemove callback is always the last one in the array.
So:
if (ctx.isPointInPath(square, event.offsetX, event.offsetY)) {
ctx.fillStyle = 'white';
}
else {
ctx.fillStyle = 'red';
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fill(square);
translated to plain english means: if the point is in square's bound, set the fillStyle to red, clear the whole screen and afterwards fill one bar with red.
What you need to do instead is checking the mouse position with every Board instance from the array. It ain't to hard though - just make the Path2D a class variable of Board and inside the callback function loop over the whole array and compare the mouse position with each Board's .square property.
Here's an example (just click on 'Run code snippet'):
let canvas = document.querySelector('#canvas'),
ctx = canvas.getContext('2d');
let b = [];
class Board {
constructor(startX, startY, height, width, angle) {
this.startX = startX;
this.startY = startY;
this.height = height;
this.width = width;
this.angle = angle;
this.square = new Path2D();
}
drawBoard() {
let canvasWidth = window.innerWidth * 0.95,
drawWidth = canvasWidth * this.width,
drawHeight = canvasWidth * this.height,
drawStartX = canvasWidth * this.startX,
drawStartY = canvasWidth * this.startY;
ctx.rotate(this.angle * Math.PI / 180);
this.square.rect(drawStartX, drawStartY, drawHeight, drawWidth);
ctx.fillStyle = 'red';
ctx.fill(this.square);
}
}
canvas.addEventListener('mousemove', function(event) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let currentSquare;
for (let i = 0; i < b.length; i++) {
currentSquare = b[i].square;
if (ctx.isPointInPath(currentSquare, event.offsetX, event.offsetY)) {
ctx.fillStyle = 'white';
} else {
ctx.fillStyle = 'red';
}
ctx.fill(currentSquare);
}
});
for (let i = 0; i < 10; i++) {
b.push(new Board(0.05 * i, 0.25, 0.04, 0.03, 0));
}
function loadFunctions() {
b.forEach(function(board) {
board.drawBoard();
})
}
loadFunctions();
<canvas id="canvas" width=500 height=300></canvas>
I made a diffuse animation and used RequestAnimationFrame to achieve it.I tried hundreds of times to improve my program.But it doesn't work!!!Where's my wrong?
function draw_point(x, y){
x += 10.5;
y += 10.5;
radius = 0;
var context = $("#canvas_graph")[0].getContext('2d');
window.webkitRequestAnimationFrame(render(x, y, radius, context));
};
function drawCircle(x, y, radius, context){
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2);
context.closePath();
context.lineWidth = 2;
context.strokeStyle = 'red';
context.stroke();
radius += 0.2;
if (radius > 10) {
radius = 0;
}
};
//The effect of diffusion is achieved by changing the attribute
function render(x ,y, radius, context){
return function step(){
var prev = context.globalCompositeOperation;
context.globalCompositeOperation = 'destination-in';
context.globalAlpha = 0.95;
context.fillRect(0, 0, 890, 890);
context.globalCompositeOperation = prev;
drawCircle(x, y, radius,context);
window.webkitRequestAnimationFrame(step);
};
};
draw_point(100, 100);
But this can be used normally.Function render through the globalAlpha attribute so that the circle is getting lighter.Newly drawn circles are getting bigger.Small rounds are becoming lighter and lighter by using the globalAlpha property again and again.
var context = $("#canvas_graph")[0].getContext('2d');
var radius = 0;
var x = 100;
var y = 100;
function drawCircle(){
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2);
context.closePath();
context.lineWidth = 2;
context.strokeStyle = 'red';
context.stroke();
radius += 0.2;
if (radius > 10) {
radius = 0;
}
};
function render(){
var prev = context.globalCompositeOperation;
context.globalCompositeOperation = 'destination-in';
context.globalAlpha = 0.95;
context.fillRect(0, 0, 890, 890);
context.globalCompositeOperation = prev;
drawCircle();
window.webkitRequestAnimationFrame(render);
};
window.webkitRequestAnimationFrame(render);
In addition,when canvas animation move up, how to restore the background?
Adjust your draw_point function like so. What you have currently is executing the render function right away, you want to pass a reference to requestAnimationFrame, not inserting a function call there
function draw_point(x, y){
x += 10.5;
y += 10.5;
radius = 0;
var context = $("#canvas_graph")[0].getContext('2d');
window.webkitRequestAnimationFrame(function() {
render(x, y, radius, context));
}
};
I have some code below for the start of a snake game that I'm making using HTML5 canvas. For some reason, the red circle that I'm temporarily using to represent my snake is drawing constantly following the path the mouse moves in. and it uses the food as a starting point. Check it out in your browser, because it's really hard to describe. All I want is for the circle to follow the mouse and leave a small trail that ends and doesn't stay on the canvas. How would I go about doing this. Thanks in advance!
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Snake 2.0</title>
</head>
<style>
</style>
<body>
<div>
<canvas id="canvas" width=500 height=500></canvas>
</div>
<script type="text/javascript">
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
makeFood();
function makeFood() {
foods = [];
for (var i = 0; i < 1; i++){
foods.push(new Food());
}
}
function Food() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.radius = 10;
}
function drawFood() {
for (var i = 0; i < 1; i++){
foods.push(new Food());
}
for (var i = 0; i < foods.length; i++){
var f = foods[i];
context.beginPath();
var grd = context.createRadialGradient(f.x, f.y, (f.radius - (f.radius - 1)), f.x + 1, f.y + 1, (f.radius));
grd.addColorStop(0, 'red');
grd.addColorStop(1, 'blue');
context.arc(f.x, f.y, f.radius, 0, 2 * Math.PI, true);
context.fillStyle = grd;
context.fill();
}
}
function makePower() {
powers = [];
for (var i = 0; i < 1; i++){
powers.push(new Power());
}
}
function Power() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.radius = 8;
}
function drawPower() {
for (var i = 0; i < powers.length; i++){
var p = powers[i];
context.beginPath();
var grd = context.createRadialGradient(p.x, p.y, (p.radius - (p.radius - 1)), p.x + 1, p.y + 1, (p.radius));
grd.addColorStop(0, 'green');
grd.addColorStop(1, 'yellow');
context.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, true);
context.fillStyle = grd;
context.fill();
}
}
canvas.addEventListener("mousemove", function(event) {
move(event);
});
function move(e) {
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);
var a = e.clientX;
var b = e.clientY;
context.arc(a, b, 20, 0, 2 * Math.PI, true);
context.fillStyle = "red";
context.fill();
}
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);
var functions = [drawFood];
var timer = setInterval(function(){
drawFood();
}, 5000);
function stop() {
clearInterval(timer);
}
canvas.addEventListener("click", stop);
//timer = setInterval(start, 1000);
//timer = setInterval(start, 5000);
</script>
</body>
</html>
You could start by adding "context.beginPath();" in your "move" function, before "context.arc(a, b, 20, 0, 2 * Math.PI, true);", line 102-103 in my editor.
function move(e) {
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);
var a = e.clientX;
var b = e.clientY;
context.beginPath();
context.arc(a, b, 20, 0, 2 * Math.PI, true);
context.fillStyle = "red";
context.fill();
}
Here is the fiddle : http://jsfiddle.net/sd5hh57b/1/
You should store the positions you move along in an array. Then a new timer should revisit those discs and redraw them in a more faded color each time it ticks, until a disc becomes black. Then it should be removed from that array.
Here is fiddle that does that.
The change in the code starts at canvas.addEventListener("mousemove",... and goes like this:
canvas.addEventListener("mousemove", function(event) {
// Replaced move function by drawDisc function,
// which needs coordinates and color intensity
drawDisc(event.clientX, event.clientY, 0xF);
});
// Array to keep track of previous positions, i.e. the trail
var trail = [];
function drawDisc(x, y, red) {
context.beginPath();
context.arc(x, y, 20, 0, 2 * Math.PI, true);
context.fillStyle = '#' + red.toString(16) + '00000';
context.fill();
// If disc is not completely faded out, push it in the trail list
if (red) {
trail.push({x: x, y: y, red: red});
}
}
// New function to regularly redraw the trail
function fadeTrail() {
var discs = trail.length;
// If there is only one disc in the trail, leave it as-is,
// it represents the current position.
if (discs > 1) {
for (var i = discs; i; i--) {
// take "oldest" disc out of the array:
disc = trail.shift();
// and draw it with a more faded color, unless it is
// the current disc, which keeps its color
drawDisc(disc.x, disc.y, disc.red - (i === 1 ? 0 : 1));
}
}
}
// New timer to fade the trail
var timerFade = setInterval(function(){
fadeTrail();
}, 10);
I think the comments will make clear what this does. Note that the colors of the discs go from 0xF00000 to 0xE00000, 0xD00000, ... , 0x000000. Except the current disc, that one keeps its 0xF00000 color all the time.
The other answers are right :
Use beginPath() at each new arc() to create a new Path and avoid context.fill() considers the whole as a single Path.
Use a trail Array to store your last positions to draw the trail.
But, the use of setTimeout and setInterval should be avoided (and even further the use of multiple ones).
Modern browsers do support requestAnimationFrame timing method, and for olders (basically IE9), you can find polyfills quite easily. It has a lot of advantages that I won't enumerate here, read the docs.
Here is a modified version of your code, which uses a requestAnimationFrame loop.
I also created two offscreen canvases to update your foods and powers, this way they won't disappear at each draw. Both will be painted in the draw function.
I changed the mousemove handler so it only updates the trail array, leaving the drawing part in the draw loop. At each call, it will set a moving flag that will let our draw function know that we are moving the mouse. Otherwise, it will start to remove old trail arcs from the Array.
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var context = canvas.getContext("2d");
// create other contexts (layer like) for your food and powers
var foodContext = canvas.cloneNode(true).getContext('2d');
var pwrContext = canvas.cloneNode(true).getContext('2d');
// a global to tell weither we are moving or not
var moving;
// a global to store our animation requests and to allow us to pause it
var raf;
// an array to store our trail position
var trail = [];
// here we can determine how much of the last position we'll keep at max (can then be updated if we ate some food)
var trailLength = 10;
// your array for the foods
var foods = [];
// a global to store the last time we drawn the food, no more setInterval
var lastDrawnFood = 0;
// start the game
draw();
function makeFood() {
foods.push(new Food());
}
function Food() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.radius = 10;
}
function drawFood() {
// clear the food Canvas (this could be done only if we ate some, avoiding the loop through all our foods at each call of this method)
foodContext.clearRect(0, 0, canvas.width, canvas.height);
foods.push(new Food());
for (var i = 0; i < foods.length; i++) {
var f = foods[i];
// draw on the food context
foodContext.beginPath();
foodContext.arc(f.x, f.y, f.radius, 0, 2 * Math.PI, true);
var foodGrd = foodContext.createRadialGradient(f.x, f.y, (f.radius - (f.radius - 1)), f.x + 1, f.y + 1, (f.radius));
foodGrd.addColorStop(0, 'red');
foodGrd.addColorStop(1, 'blue');
foodContext.fillStyle = foodGrd;
foodContext.fill();
}
}
// I'll let you update this one
function makePower() {
powers = [];
for (var i = 0; i < 1; i++) {
powers.push(new Power());
}
}
function Power() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.radius = 8;
}
function drawPower() {
pwrContext.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < powers.length; i++) {
var p = powers[i];
var pwrGrd = pwrContext.createRadialGradient(p.x, p.y, (p.radius - (p.radius - 1)), p.x + 1, p.y + 1, (p.radius));
pwrGrd.addColorStop(0, 'green');
pwrGrd.addColorStop(1, 'yellow');
pwrContext.beginPath();
pwrContext.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, true);
pwrContext.fillStyle = pwrGrd;
pwrContext.fill();
}
}
// the event object is already passed, no need for an anonymous function here
canvas.addEventListener("mousemove", move);
function move(e) {
// we paused the game, don't update our position
if (!raf) return;
// update the snake
var a = e.clientX - canvas.offsetLeft;
var b = e.clientY - canvas.offsetTop;
trail.splice(0, 0, {
x: a,
y: b
});
// tell our draw function that we moved
moving = true;
}
function draw(time) {
// our food timer
if (time - lastDrawnFood > 5000) {
lastDrawnFood = time;
drawFood();
}
// clear the canvas
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);
// draw the food
context.drawImage(foodContext.canvas, 0, 0);
// draw the power
context.drawImage(pwrContext.canvas, 0, 0);
//draw the snake
for (var i = 0; i < trail.length; i++) {
// decrease the opacity
opacity = 1 - (i / trail.length);
context.fillStyle = "rgba(255, 0,0," + opacity + ")";
// don't forget to create a new Path for each circle
context.beginPath();
context.arc(trail[i].x, trail[i].y, 20, 0, 2 * Math.PI, true);
context.fill();
}
// if we're not moving or if our trail is too long
if ((!moving || trail.length > trailLength) && trail.length > 1)
// remove the oldest trail circle
trail.pop();
// we're not moving anymore
moving = false;
// update the animation request
raf = requestAnimationFrame(draw);
}
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);
function toggleStop() {
if (!raf) {
// restart the animation
raf = window.requestAnimationFrame(draw);
} else {
// cancel the next call
cancelAnimationFrame(raf);
raf = 0;
}
}
canvas.addEventListener("click", toggleStop);
html, body{margin:0;}
<canvas id="canvas" width=500 height=500></canvas>
On my canvas animation, I have a problem where my circles are getting drawn "without the metaphorical pen" lifting from the canvas.
I need a way to stop the function and just draw one circle than another one.
Here is my JSFiddle (Warning: uses 100% of one logical processor core/thread).
JavaScript:
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var size = 19;
var size_two = 19;
function start(){
requestAnimationFrame(start);
size++;
context.arc(95, 85, size, 0, 2*Math.PI);
context.stroke();
}
function othercircle(){
requestAnimationFrame(othercircle);
size_two++;
context.arc(500, 300, size_two, 0, 3*Math.PI);
}
start();
othercircle();
The other answers are good, but I wanted to highlight why your unwanted line is appearing.
Your unwanted connecting line
The unwanted connecting line is created because you must call context.beginPath() before drawing each arc. If you don't call begainPath the browser assumes you want to connect the 2 circle paths.
context.beginPath()
context.arc(95, 85, size, 0, 2*Math.PI);
context.stroke();
context.beginPath();
context.arc(200, 200, size_two, 0, 3*Math.PI);
context.stroke();
Just a couple more notes
Your othercircle is drawing an arc that's 3 * PI. 2*PI is a complete circle and any value above 2*PI will not add to the circle.
If it was your intent to draw an expanding stroke-circle, then you should clear the canvas at the start of each animation loop (before drawing the expanded circles).
One requestAnimationFrame is enough. You can put both your circle and your othercircle code in one requestAnimationFrame.
Example code and a Demo: http://jsfiddle.net/m1erickson/62mFF/
var sizeCounter=19;
var maxSizeCounter=60;
var size = sizeCounter;
var maxSize=40;
var size_two = sizeCounter;
var maxSizeTwo=60;
function start(){
if(sizeCounter<maxSizeCounter){ requestAnimationFrame(start); }
// clear the canvas if you want strokes instead of filled circles
context.clearRect(0,0,canvas.width,canvas.height);
context.beginPath()
context.arc(95, 85, size, 0, 2*Math.PI);
context.stroke();
if(size<maxSize){ size++; }
context.beginPath();
context.arc(200, 200, size_two, 0, 3*Math.PI);
context.stroke();
if(size_two<maxSizeTwo){ size_two++; }
sizeCounter++;
}
start();
Then use a condition to stop the recursive call, or just omit size++ and size_two++ from your code.
Your functions don't have all the information for a complete circle, thus the line between the two circles. Below will make two circles without the line between them.
function start(){
requestAnimationFrame(start);
size++;
context.beginPath();
context.arc(95, 85, size, 0, 2*Math.PI);
context.closePath();
context.fill();
}
function othercircle(){
requestAnimationFrame(othercircle);
size_two++;
context.beginPath();
context.arc(500, 300, size_two, 0, 3*Math.PI);
context.closePath();
context.fill();
}
Here is an updated jsFiddle
http://jsfiddle.net/gamealchemist/4nQCa/5
I think it's simpler to go object in your case : define a Circle class that will update with time and draw itself :
/// Circle class.
// has update(dt) and draw as public method.
function Circle(x, y, initSize, endSize, duration, color) {
this.x = x;
this.y = y;
this.size = initSize;
this.endSize = endSize;
this.color = color;
this.speed = (endSize - initSize) / duration;
this.update = function (dt) {
if (this.speed == 0) return;
this.size += dt * this.speed;
if (this.size > this.endSize) {
this.size = this.endSize;
this.speed = 0;
}
}
this.draw = function () {
context.beginPath();
context.strokeStyle = this.color;
context.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
context.stroke();
}
}
then hold all your animated objects within an array
var scene=[];
And have the objects animated/drawn :
function animate() {
// keep alive
requestAnimationFrame(animate);
// handle time
var callTime = Date.now();
var dt = callTime - lastTime;
lastTime = callTime;
// clear screen
context.clearRect(0, 0, canvasWidth, canvasHeight);
context.fillText('Click anywhere !',40, 80);
// draw
for (var i = 0; i < scene.length; i++) {
var thisObject = scene[i];
thisObject.draw();
}
// update
for (var i = 0; i < scene.length; i++) {
var thisObject = scene[i];
thisObject.update(dt);
}
}
var lastTime = Date.now();
animate();
With this scheme it's easy to add/remove objects. For instance to add circles on mouse click :
addEventListener('mousedown', function (e) {
var x = e.clientX,
y = e.clientY;
var color = 'hsl(' + Math.floor(Math.random() * 360) + ',80%, 85%)';
var newCircle = new Circle(x, y, 20, 100, 1000, color);
scene.push(newCircle);
});
Alright guys, I'm sure this has been asked before, but I couldn't find anything that directly related to what I was doing. So I have these 4 self drawing circles (or gauges.) Each one has it's own value, and I've been looking through just nit picking through codes and books to build this. My question I need to figure out is how I would go about putting in a count up? Basically I want a counter to go from 1 - x (x being the degree of the circle it's in). I've included my js and HTML 5 for you guys to look at.
HTML
<canvas id="a" width="300" height="300"></canvas>
<script>
var canvas = document.getElementById('a');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var startAngle = 1.5 * Math.PI;
var endAngle = 3.2 * Math.PI;
var counterClockwise = false;
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, counterClockwise);
context.lineWidth = 15;
// line color
context.strokeStyle = 'black';
context.stroke();
</script>
Canvas.JS
$(document).ready(function(){
function animate(elementId, endPercent) {
var canvas = document.getElementById(elementId);
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var curPerc = 0;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
context.lineWidth = 15;
context.strokeStyle = '#85c3b8';
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 10;
function render(current) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
context.stroke();
curPerc++;
if (curPerc < endPercent) {
requestAnimationFrame(function () {
render(curPerc / 100);
});
}
}
render();
}
$(window).scroll(function(){
if($(this).scrollTop()<1600){
animate('a', 85);
animate('b', 95);
animate('c', 80);
animate('d', 75);
}
});
});
Keep in mind that I am very new to canvas, I appreciate all the help guys!
Demo: http://jsfiddle.net/m1erickson/mYKp5/
You can save your gauges as objects in an array:
var guages=[];
guages.push({ x:50, y:100, radius:40, start:0, end:70, color:"blue" });
guages.push({ x:200, y:100, radius:40, start:0, end:90, color:"green" });
guages.push({ x:50, y:225, radius:40, start:0, end:35, color:"gold" });
guages.push({ x:200, y:225, radius:40, start:0, end:55, color:"purple" });
The render function takes a guage object draws its progress
function render(guage,percent) {
var pct=percent/100;
var extent=parseInt((guage.end-guage.start)*pct);
var current=(guage.end-guage.start)/100*PI2*pct-quart;
ctx.beginPath();
ctx.arc(guage.x,guage.y,guage.radius,-quart,current);
ctx.strokeStyle=guage.color;
ctx.stroke();
ctx.fillStyle=guage.color;
ctx.fillText(extent,guage.x-15,guage.y+5);
}
And the animation loop asks render to draw all gauges from 0-100 percent of their full values
function animate() {
// if the animation is not 100% then request another frame
if(percent<100){
requestAnimationFrame(animate);
}
// redraw all guages with the current percent
drawAll(percent);
// increase percent for the next frame
percent+=1;
}
function drawAll(percent){
// clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw all the guages
for(var i=0;i<guages.length;i++){
render(guages[i],percent);
}
}