Canvas bouncing balls showing an elliptical black ball - javascript

JSFiddle
Intended behavior
Sample values: Velocity: 10; Angle: 45; Color: red; Radius: 50.
On clicking "Shoot!" button, ball should move until it finally disappears behind one of the walls. Note that I want to simulate real world ball with gravity.
Each time we click Shoot, one more ball should be added to the balls array which will also be drawn.
What happens
A black ellipse is shown on clicking shoot once/multiple time. No console errors seen.
Code:
(function () {
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
WIDTH = canvas.width,
HEIGHT = canvas.height;
// our ball object
function Ball(radius, color) {
this.radius = radius;
this.color = color;
this.x = 50; // start coordinates
this.y = 50;
this.velX = 0;
this.velY = 0;
this.accX = 0;
this.accY = 0;
this.gravity = 0;
this.start = function (angle, velocity) {
this.gravity = 9.8;
this.angle = angle / 180 * Math.PI; // convert to radians
this.velX = velocity * Math.cos(this.angle);
this.velY = velocity * Math.sin(this.angle);
this.accX = 0; // zero intially
this.accY = 0; // TODO: set option for user to set himself
};
this.update = function () {
this.velY -= this.gravity;
};
this.draw = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.fillSyle = this.color;
ctx.fill();
ctx.closePath();
}
}
// balls array
var balls = [];
document.querySelector("input[type='button']").onclick = function () {
var color = gId("color").value, // getElementById; defined in jsfiddle
velocity = gId("velocity").value,
angle = gId("angle").value,
radius = gId("radius").value;
var ball = new Ball(radius, color);
ball.start(angle, velocity);
balls.push(ball);
};
setInterval(function () {
for (var i = 0, len = balls.length; i < len; i++) {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
balls[i].draw();
balls[i].update();
}
}, 1000 / 60); // 1000/x depicts x fps
I have no idea why it doesn't work. System: Windows 7 on Chrome/Firefox latest.
Any help is appreciated. Please comment for more information.

1) Set width and height attributes on your canvas element instead of applying a css style i.e:
<canvas width="400px" height="400px">You don't support canvas.</canvas>
2) divide the value of gravity by 60 because your update function is invoked every 1/60th of a second i.e:
this.start = function (angle, velocity) {
this.gravity = 9.8 / 60;
this.angle = angle / 180 * Math.PI; // convert to radians
this.velX = velocity * Math.cos(this.angle);
this.velY = velocity * Math.sin(this.angle);
this.accX = 0; // zero intially
this.accY = 0; // TODO: set option for user to set himself
};
3) change the update function to:
this.update = function () {
this.velY += this.gravity;
this.x += this.velX;
this.y += this.velY;
};
4) move the ctx.clearRect method outside the for loop otherwise you will only see one ball animating always i.e
setInterval(function () {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
for (var i = 0, len = balls.length; i < len; i++) {
balls[i].draw();
balls[i].update();
}
}, 1000 / 60); // 1000/x depicts x fps
Here the updated js-fiddle: http://jsfiddle.net/km6ozj6L/1/

Related

Canvas beginPath() is not clearing rect before each frame

I want to animate a rect on a canvas. It technically works, but the canvas is not clearing before each frame and it leaves a mark on the ground (sort of like a snail). I have done research and everything seems to point to the use of ctx.beginPath() as the solution to my problem, but when I try to use it here, it doesn't work. What am I doing wrong?
Here is the raw javascript:
// create a canvas element
var canvas = document.createElement("canvas");
// attach element to DOM
document.getElementsByTagName("body")[0].appendChild(canvas);
// get the canvas context (this is the part we draw to)
var ctx = canvas.getContext("2d");
var bug = new Bug(0,0);
function setup() {
// setup the canvas size to match the window
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// set the 0,0 point to the middle of the canvas
ctx.translate(canvas.width / 2, canvas.height / 2);
}
function draw() { //Do the drawing
ctx.beginPath();
bug.update();
bug.draw();
window.requestAnimationFrame(function(){draw()});
}
// start enterFrame loop
window.requestAnimationFrame(draw);
// force running setup
setup();
// re-setup canvas when the size of the window changes
window.addEventListener("resize", setup);
// sin(pi) == 0
// cos(pi) == -1
// sin(2pi) == 0
// cos(2pi) == 1
// degrees to radians: {{ deg * (pi/180) = rad }}
function randomRange(max, min) {
return Math.floor(Math.random() * (max - min) + min);
}
function Bug(x, y) {
this.x = x;
this.y = y;
this.jitter = 10;
this.speed = 1;
this.deg = 0;
this.rad = 0;
this.update = function() {
//update degrees
this.deg += randomRange(this.jitter, -this.jitter);
//convert degrees into radians
this.rad = this.deg * (Math.PI/180);
//update coordinates
this.x += this.speed * (Math.cos(this.rad));
this.y += this.speed * (Math.sin(this.rad));
};
this.draw = function() {
ctx.beginPath();
ctx.rect(this.x, this.y, 50, 50);
ctx.fill();
};
}
The beginPath function doesn't clear the screen it starts a path to draw, to clear the screen you should use clearRect instead and in your specific situation you should use:
ctx.clearRect(-canvas.width, -canvas.height, canvas.width*2, canvas.height*2);

Loop Background Image Animation in Canvas

I am new to Canvas and want to loop background Image in the below smoke effect. On searching, I have found an example that how we can loop background Image in canvas Link to looping animation so I tried integrating the looping code with the smoke effect but no success. Any help will be appreciated.
// Create an array to store our particles
var particles = [];
// The amount of particles to render
var particleCount = 60;
// The maximum velocity in each direction
var maxVelocity = 2;
// The target frames per second (how often do we want to update / redraw the scene)
var targetFPS = 33;
// Set the dimensions of the canvas as variables so they can be used.
var canvasWidth = window.innerWidth;
var canvasHeight = window.innerHeight;
// borders for particles on top and bottom
var borderTop = 0.01 * canvasHeight;
var borderBottom = 0.99 * canvasHeight;
// Create an image object (only need one instance)
var imageObj = new Image();
var looping = false;
var totalSeconds = 0;
// Once the image has been downloaded then set the image on all of the particles
imageObj.onload = function() {
particles.forEach(function(particle) {
particle.setImage(imageObj);
});
};
// Once the callback is arranged then set the source of the image
imageObj.src = "https://image.ibb.co/fdpeJF/Smoke.png";
// A function to create a particle object.
function Particle(context) {
// Set the initial x and y positions
this.x = 0;
this.y = 0;
// Set the initial velocity
this.xVelocity = 0;
this.yVelocity = 0;
// Set the radius
this.radius = 5;
// Store the context which will be used to draw the particle
this.context = context;
// The function to draw the particle on the canvas.
this.draw = function() {
// If an image is set draw it
if (this.image) {
this.context.drawImage(this.image, this.x - 128, this.y - 128);
// If the image is being rendered do not draw the circle so break out of the draw function
return;
}
// Draw the circle as before, with the addition of using the position and the radius from this object.
this.context.beginPath();
this.context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
this.context.fillStyle = "rgba(0, 255, 255, 1)";
this.context.fill();
this.context.closePath();
};
// Update the particle.
this.update = function() {
// Update the position of the particle with the addition of the velocity.
this.x += this.xVelocity;
this.y += this.yVelocity;
// Check if has crossed the right edge
if (this.x >= canvasWidth) {
this.xVelocity = -this.xVelocity;
this.x = canvasWidth;
}
// Check if has crossed the left edge
else if (this.x <= 0) {
this.xVelocity = -this.xVelocity;
this.x = 0;
}
// Check if has crossed the bottom edge
if (this.y >= borderBottom) {
this.yVelocity = -this.yVelocity;
this.y = borderBottom;
}
// Check if has crossed the top edge
else if (this.y <= borderTop) {
this.yVelocity = -this.yVelocity;
this.y = borderTop;
}
};
// A function to set the position of the particle.
this.setPosition = function(x, y) {
this.x = x;
this.y = y;
};
// Function to set the velocity.
this.setVelocity = function(x, y) {
this.xVelocity = x;
this.yVelocity = y;
};
this.setImage = function(image) {
this.image = image;
};
}
// A function to generate a random number between 2 values
function generateRandom(min, max) {
return Math.random() * (max - min) + min;
}
// The canvas context if it is defined.
var context;
// Initialise the scene and set the context if possible
function init() {
var canvas = document.getElementById('myCanvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
if (canvas.getContext) {
// Set the context variable so it can be re-used
context = canvas.getContext('2d');
// Create the particles and set their initial positions and velocities
for (var i = 0; i < particleCount; ++i) {
var particle = new Particle(context);
// Set the position to be inside the canvas bounds
particle.setPosition(generateRandom(0, canvasWidth), generateRandom(borderTop, borderBottom));
// Set the initial velocity to be either random and either negative or positive
particle.setVelocity(generateRandom(-maxVelocity, maxVelocity), generateRandom(-maxVelocity, maxVelocity));
particles.push(particle);
context.clearRect(0, 0, canvas.width, canvas.height);
}
} else {
alert("Please use a modern browser");
}
}
// The function to draw the scene
function draw() {
// background image
context.globalAlpha = 1;
context.globalCompositeOperation = 'source-over';
context.drawImage(backImg, 0, 0, canvasWidth, canvasHeight);
context.fillStyle = "rgba(255,255,255, .5)";
context.fillRect(0, 0, canvasWidth, canvasHeight);
context.globalAlpha = 0.75;
context.globalCompositeOperation = 'soft-lights';
// Fog layer
// Go through all of the particles and draw them.
particles.forEach(function(particle) {
particle.draw();
});
}
// Update the scene
function update() {
particles.forEach(function(particle) {
particle.update();
});
}
// Initialize the scene
init();
backImg = new Image();
backImg.src = 'https://image.ibb.co/cTOOdF/e2VZQY.jpg';
// If the context is set then we can draw the scene (if not then the browser does not support canvas)
if (context) {
setInterval(function() {
// Update the scene befoe drawing
update();
// Draw the scene
draw();
}, 1000 / targetFPS);
}
<canvas id="myCanvas" ></canvas>
I just added a few lines. Hopefully you can spot them. I commented everything I added.
// Create an array to store our particles
var particles = [];
// The amount of particles to render
var particleCount = 60;
// The maximum velocity in each direction
var maxVelocity = 2;
// The target frames per second (how often do we want to update / redraw the scene)
var targetFPS = 33;
// Set the dimensions of the canvas as variables so they can be used.
var canvasWidth = window.innerWidth;
var canvasHeight = window.innerHeight;
// borders for particles on top and bottom
var borderTop = 0.01 * canvasHeight;
var borderBottom = 0.99 * canvasHeight;
// Create an image object (only need one instance)
var imageObj = new Image();
// x position of scrolling image
var imageX = 0;
var looping = false;
var totalSeconds = 0;
// Once the image has been downloaded then set the image on all of the particles
imageObj.onload = function() {
particles.forEach(function(particle) {
particle.setImage(imageObj);
});
};
// Once the callback is arranged then set the source of the image
imageObj.src = "https://image.ibb.co/fdpeJF/Smoke.png";
// A function to create a particle object.
function Particle(context) {
// Set the initial x and y positions
this.x = 0;
this.y = 0;
// Set the initial velocity
this.xVelocity = 0;
this.yVelocity = 0;
// Set the radius
this.radius = 5;
// Store the context which will be used to draw the particle
this.context = context;
// The function to draw the particle on the canvas.
this.draw = function() {
// If an image is set draw it
if (this.image) {
this.context.drawImage(this.image, this.x - 128, this.y - 128);
// If the image is being rendered do not draw the circle so break out of the draw function
return;
}
// Draw the circle as before, with the addition of using the position and the radius from this object.
this.context.beginPath();
this.context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
this.context.fillStyle = "rgba(0, 255, 255, 1)";
this.context.fill();
this.context.closePath();
};
// Update the particle.
this.update = function() {
// Update the position of the particle with the addition of the velocity.
this.x += this.xVelocity;
this.y += this.yVelocity;
// Check if has crossed the right edge
if (this.x >= canvasWidth) {
this.xVelocity = -this.xVelocity;
this.x = canvasWidth;
}
// Check if has crossed the left edge
else if (this.x <= 0) {
this.xVelocity = -this.xVelocity;
this.x = 0;
}
// Check if has crossed the bottom edge
if (this.y >= borderBottom) {
this.yVelocity = -this.yVelocity;
this.y = borderBottom;
}
// Check if has crossed the top edge
else if (this.y <= borderTop) {
this.yVelocity = -this.yVelocity;
this.y = borderTop;
}
};
// A function to set the position of the particle.
this.setPosition = function(x, y) {
this.x = x;
this.y = y;
};
// Function to set the velocity.
this.setVelocity = function(x, y) {
this.xVelocity = x;
this.yVelocity = y;
};
this.setImage = function(image) {
this.image = image;
};
}
// A function to generate a random number between 2 values
function generateRandom(min, max) {
return Math.random() * (max - min) + min;
}
// The canvas context if it is defined.
var context;
// Initialise the scene and set the context if possible
function init() {
var canvas = document.getElementById('myCanvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
if (canvas.getContext) {
// Set the context variable so it can be re-used
context = canvas.getContext('2d');
// Create the particles and set their initial positions and velocities
for (var i = 0; i < particleCount; ++i) {
var particle = new Particle(context);
// Set the position to be inside the canvas bounds
particle.setPosition(generateRandom(0, canvasWidth), generateRandom(borderTop, borderBottom));
// Set the initial velocity to be either random and either negative or positive
particle.setVelocity(generateRandom(-maxVelocity, maxVelocity), generateRandom(-maxVelocity, maxVelocity));
particles.push(particle);
context.clearRect(0, 0, canvas.width, canvas.height);
}
} else {
alert("Please use a modern browser");
}
}
// The function to draw the scene
function draw() {
// background image
context.globalAlpha = 1;
context.globalCompositeOperation = 'source-over';
// draw twice to cover wrap around
context.drawImage(backImg, imageX, 0, canvasWidth, canvasHeight);
context.drawImage(backImg, imageX + canvasWidth, 0, canvasWidth, canvasHeight);
context.fillStyle = "rgba(255,255,255, .5)";
context.fillRect(0, 0, canvasWidth, canvasHeight);
context.globalAlpha = 0.75;
context.globalCompositeOperation = 'soft-light';
// Fog layer
// Go through all of the particles and draw them.
particles.forEach(function(particle) {
particle.draw();
});
}
// Update the scene
function update() {
// incrementally change image position of background to scroll left
imageX -= maxVelocity;
if (imageX < -canvasWidth) {
imageX += canvasWidth;
}
particles.forEach(function(particle) {
particle.update();
});
}
// Initialize the scene
init();
backImg = new Image();
backImg.src = 'https://image.ibb.co/cTOOdF/e2VZQY.jpg';
// If the context is set then we can draw the scene (if not then the browser does not support canvas)
if (context) {
setInterval(function() {
// Update the scene befoe drawing
update();
// Draw the scene
draw();
}, 1000 / targetFPS);
}
<canvas id="myCanvas"></canvas>
Just to add to the answer given some additional improvements to the code structure.
Use requestAnimationFrame to call render calls.
Don't expose properties of objects if not needed.
Don't use forEach iteration in time critical code. Use for loops.
Use constants where ever possible.
Comments that state the obvious are just noise in the source code making it harder to read. Limit comment to abstracts that may not be obvious to another programmer reading the code.
eg
// If an image is set draw it
if (this.image) {
Really is that comment of any use to anyone. Comments should help not degrade the readability of code.
Also the original code tried to set the global composite operations to soft-lights this is not a know operation. I corrected it to soft-light which can on some machines, be a very slow render operation. It may pay to selected another operation for machines that are slow. This can be done by simply monitoring the render time of particles and switching operation type is too slow.
A quick rewrite of the OP's code.
const particles = [];
const particleCount = 60;
const maxVelocity = 2;
var canvasWidth = innerWidth;
var canvasHeight = innerHeight;
var borderTop = 0.01 * canvasHeight;
var borderBottom = 0.99 * canvasHeight;
var ctx;
const backgroundColor = "rgba(255,255,255, .5)";
const backgroundSpeed = -0.1;
var looping = false;
var totalSeconds = 0;
var lastTime = 0;
var frameTime = (1000 / 30) - (1000 / 120); // one quater frame short to
// allow for timing error
var imageCount = 0;
const backImg = new Image();
const imageObj = new Image();
backImg.src = 'https://image.ibb.co/cTOOdF/e2VZQY.jpg';
imageObj.src = "https://image.ibb.co/fdpeJF/Smoke.png";
backImg.onload = imageObj.onload = imageLoad;
function imageLoad(){
imageCount += 1;
if(imageCount === 2){
init();
}
}
function init() {
var canvas = myCanvas;
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx = canvas.getContext('2d');
for (var i = 0; i < particleCount; i += 1) {
particles.push(new Particle(ctx));
}
lastTime = performance.now();
requestAnimationFrame(mainLoop);
}
function mainLoop(time){
if(time-lastTime > frameTime){
lastTime = time;
update();
draw(time);
}
requestAnimationFrame(mainLoop);
}
const rand = (min, max) => Math.random() * (max - min) + min; // names are best short (short only without ambiguity)
function Particle(ctx) {
var x, y, xVel, yVel, radius, image;
const color = "rgba(0, 255, 255, 1)";
x = rand(0, canvasWidth),
y = rand(borderTop, borderBottom);
xVel = rand(-maxVelocity, maxVelocity);
yVel = rand(-maxVelocity, maxVelocity);
radius = 5;
image = imageObj;
this.draw = function () { ctx.drawImage(image, x - 128, y - 128) }
this.update = function () {
x += xVel;
y += yVel;
if (x >= canvasWidth) {
xVel = -xVel;
x = canvasWidth;
}
else if (x <= 0) {
xVel = -xVel;
x = 0;
}
if (y >= borderBottom) {
yVel = -yVel;
y = borderBottom;
}
else if (y <= borderTop) {
yVel = -yVel;
y = borderTop;
}
}
}
function draw(time) {
var i,x;
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = 'source-over';
x = time * backgroundSpeed;
x = ((x % canvasWidth) + canvasWidth) % canvasWidth;
ctx.drawImage(backImg, x, 0, canvasWidth, canvasHeight);
ctx.drawImage(backImg, x - canvasWidth, 0, canvasWidth, canvasHeight);
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.globalAlpha = 0.75;
ctx.globalCompositeOperation = 'soft-light';
for(i = 0; i < particles.length; i += 1){
particles[i].draw();
}
}
function update() {
for(i = 0; i < particles.length; i += 1){
particles[i].update();
}
}
canvas {
position : absolute;
top : 0px;
left : 0px;
}
<canvas id=myCanvas></canvas>

Canvas line drawing animation

I am new learner of animation using HTML5 Canvas. I am struggling to create line drawing animation in a canvas with desired length of a line.
Here is the code
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
width = canvas.width = window.innerWidth,
height = canvas.height = window.innerHeight;
var x = 200;
var y = 200;
draw();
update();
function draw() {
context.beginPath();
context.moveTo(100, 100);
context.lineTo(x, y);
context.stroke();
}
function update() {
context.clearRect(0, 0, width, height);
x = x + 1;
y = y + 1;
draw();
requestAnimationFrame(update);
}
html,
body {
margin: 0px;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
The line is growing on Canvas in the above code. But how to achieve that the 200px wide line and animate the movement in x and y direction. And the same animation with multiple lines using for loop and move them in different direction.
Check the reference image ....
Need to move each line in a different direction
Thanks in advance
Find a new reference image which i want to achieve
You need to either use transforms or a bit of trigonometry.
Transforms
For each frame:
Reset transforms and translate to center
Clear canvas
Draw line from center to the right
Rotate x angle
Repeat from step 2 until all lines are drawn
var ctx = c.getContext("2d");
var centerX = c.width>>1;
var centerY = c.height>>1;
var maxLength = Math.min(centerX, centerY); // use the shortest direction for demo
var currentLength = 0; // current length, for animation
var lenStep = 1; // "speed" of animation
function render() {
ctx.setTransform(1,0,0,1, centerX, centerY);
ctx.clearRect(-centerX, -centerY, c.width, c.height);
ctx.beginPath();
for(var angle = 0, step = 0.1; angle < Math.PI * 2; angle += step) {
ctx.moveTo(0, 0);
ctx.lineTo(currentLength, 0);
ctx.rotate(step);
}
ctx.stroke(); // stroke all at once
}
(function loop() {
render();
currentLength += lenStep;
if (currentLength < maxLength) requestAnimationFrame(loop);
})();
<canvas id=c></canvas>
You can use transformation different ways, but since you're learning I kept it simple in the above code.
Trigonometry
You can also calculate the line angles manually using trigonometry. Also here you can use different approaches, ie. if you want to use delta values, vectors or brute force using the math implicit.
For each frame:
Reset transforms and translate to center
Clear canvas
Calculate angle and direction for each line
Draw line
var ctx = c.getContext("2d");
var centerX = c.width>>1;
var centerY = c.height>>1;
var maxLength = Math.min(centerX, centerY); // use the shortest direction for demo
var currentLength = 0; // current length, for animation
var lenStep = 1; // "speed" of animation
ctx.setTransform(1,0,0,1, centerX, centerY);
function render() {
ctx.clearRect(-centerX, -centerY, c.width, c.height);
ctx.beginPath();
for(var angle = 0, step = 0.1; angle < Math.PI * 2; angle += step) {
ctx.moveTo(0, 0);
ctx.lineTo(currentLength * Math.cos(angle), currentLength * Math.sin(angle));
}
ctx.stroke(); // stroke all at once
}
(function loop() {
render();
currentLength += lenStep;
if (currentLength < maxLength) requestAnimationFrame(loop);
})();
<canvas id=c></canvas>
Bonus animation to play around with (using the same basis as above):
var ctx = c.getContext("2d", {alpha: false});
var centerX = c.width>>1;
var centerY = c.height>>1;
ctx.setTransform(1,0,0,1, centerX, centerY);
ctx.lineWidth = 2;
ctx.strokeStyle = "rgba(0,0,0,0.8)";
ctx.shadowBlur = 16;
function render(time) {
ctx.globalAlpha=0.77;
ctx.fillRect(-500, -500, 1000, 1000);
ctx.globalAlpha=1;
ctx.beginPath();
ctx.rotate(0.025);
ctx.shadowColor = "hsl(" + time*0.1 + ",100%,75%)";
ctx.shadowBlur = 16;
for(var angle = 0, step = Math.PI / ((time % 200) + 50); angle < Math.PI * 2; angle += step) {
ctx.moveTo(0, 0);
var len = 150 + 150 * Math.cos(time*0.0001618*angle*Math.tan(time*0.00025)) * Math.sin(time*0.01);
ctx.lineTo(len * Math.cos(angle), len * Math.sin(angle));
}
ctx.stroke();
ctx.globalCompositeOperation = "lighter";
ctx.shadowBlur = 0;
ctx.drawImage(ctx.canvas, -centerX, -centerY);
ctx.drawImage(ctx.canvas, -centerX, -centerY);
ctx.globalCompositeOperation = "source-over";
}
function loop(time) {
render(time);
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
body {margin:0;background:#222}
<canvas id=c width=640 height=640></canvas>
Here is what I think you are describing...
window.onload = function() {
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
width = canvas.width = 400,
height = canvas.height = 220,
xcenter = 200,
ycenter = 110,
radius = 0,
radiusmax = 100,
start_angle1 = 0,
start_angle2 = 0;
function toRadians(angle) {
return angle * (Math.PI / 180);
}
function draw(x1, y1, x2, y2) {
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
}
function drawWheel(xc, yc, start_angle, count, rad) {
var inc = 360 / count;
for (var angle = start_angle; angle < start_angle + 180; angle += inc) {
var x = Math.cos(toRadians(angle)) * rad;
var y = Math.sin(toRadians(angle)) * rad;
draw(xc - x, yc - y, xc + x, yc + y);
}
}
function update() {
start_angle1 += 0.1;
start_angle2 -= 0.1;
if(radius<radiusmax) radius++;
context.clearRect(0, 0, width, height);
drawWheel(xcenter, ycenter, start_angle1, 40, radius);
drawWheel(xcenter, ycenter, start_angle2, 40, radius);
requestAnimationFrame(update);
}
update();
};
html,
body {
margin: 0px;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
This is one that is a variable length emerging pattern. It has a length array element for each spoke in the wheel that grows at a different rate. You can play with the settings to vary the results:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = canvas.width = window.innerWidth;
var height = canvas.height = window.innerHeight;
var xcenter = width/4;
var ycenter = height/2;
var radius;
var time;
if(width>height) {
radius = height*0.4;
}
else {
radius = width*0.4;
}
var start_angle1 = 0;
var start_angle2 = 0;
function toRadians (angle) {
return angle * (Math.PI / 180);
}
function draw(x1,y1,x2,y2) {
context.beginPath();
context.moveTo(x1,y1);
context.lineTo(x2,y2);
context.stroke();
}
var radmax=width;
var rads = [];
var radsinc = [];
function drawWheel(xc,yc,start_angle,count,rad) {
var inc = 360/count;
var i=0;
for(var angle=start_angle; angle < start_angle+180; angle +=inc) {
var x = Math.cos(toRadians(angle)) * rads[rad+i];
var y = Math.sin(toRadians(angle)) * rads[rad+i];
draw(xc-x,yc-y,xc+x,yc+y);
rads[rad+i] += radsinc[i];
if(rads[rad+i] > radmax) rads[rad+i] = 1;
i++;
}
}
function update() {
var now = new Date().getTime();
var dt = now - (time || now);
time = now;
start_angle1 += (dt/1000) * 10;
start_angle2 -= (dt/1000) * 10;
context.clearRect(0,0,width,height);
drawWheel(xcenter,ycenter,start_angle1,50,0);
drawWheel(xcenter,ycenter,start_angle2,50,50);
requestAnimationFrame(update);
}
function init() {
for(var i=0;i<100;i++) {
rads[i] = 0;
radsinc[i] = Math.random() * 10;
}
}
window.onload = function() {
init();
update();
};
html, body {
margin: 0px;
}
canvas {
width:100%;
height:200px;
display: block;
}
<canvas id="canvas"></canvas>

Get boundaries (position) of canvas drawn context lines

I'm making a little testing thing for physics. I have drawn a circle with lines using canvas and context:
function drawAngles () {
var d = 50; //start line at (10, 20), move 50px away at angle of 30 degrees
var angle = 80 * Math.PI/180;
ctx.beginPath();
ctx.moveTo(300,0);
ctx.lineTo(300,600); //x, y
ctx.moveTo(0,300);
ctx.lineTo(600,300);
ctx.arc(300,300,300,0,2*Math.PI);
ctx.stroke();
}
I want to somehow get the curved boundaries of the circle so I can tell if the ball element has collided.
If I'm testing boundaries on a typical div, I can just do this:
var divCoords= $(".boundingBoxDiv").position();
var top = divCoords.top;
etc...
How do I do this with context lines?
Here's an image... the ball should bounce off of the circle.
Live Demo
This is pretty easy to accomplish, in a radius based collision you just check the distance if the distance is closer than the radius the objects have collided. In this instance we need to do the opposite, if the objects distance is greater than the boundary radius we need to change the angle to keep the objects in.
So first we need to identify our boundary center points, and boundary radius,
var boundaryRadius = 300,
boundaryX = 300,
boundaryY = 300;
Later on in the ball.update method I check against these values.
var dx = boundaryX - this.x,
dy = boundaryY - this.y
We need to get the distance from our ball and the boundaries center point.
dist = Math.sqrt(dx * dx + dy * dy);
Now we need to get the velocity based on our current angle
this.radians = this.angle * Math.PI/ 180;
this.velX = Math.cos(this.radians) * this.speed;
this.velY = Math.sin(this.radians) * this.speed;
Next we check if we are still inside of the boundaries. In this instance if our distance is less than 300 - our own radius (which is 10) so 290, then keep moving.
if (dist < boundaryRadius-this.radius) {
this.x += this.velX;
this.y += this.velY;
Else if our distance is greater than 290, we need to first move back a bit so we aren't colliding, then we just change our heading angle. You can do this in much fancier ways to actual calculate where you should bounce to, in this example I just make it the opposite angle with a tad bit of randomness.
} else {
this.x -= this.velX;
this.y -= this.velY;
this.angle += 180+Math.random()*45;
}
Code in its entirety.
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
width = 600,
height = 600;
canvas.width = width;
canvas.height = height;
var boundaryRadius = 300,
boundaryX = 300,
boundaryY = 300;
var Ball = function (x, y, speed) {
this.x = x || 0;
this.y = y || 0;
this.radius = 10;
this.speed = speed || 10;
this.color = "rgb(255,0,0)";
this.angle = Math.random() * 360;
this.radians = this.angle * Math.PI/ 180;
this.velX = 0;
this.velY = 0;
}
Ball.prototype.update = function () {
var dx = boundaryX - this.x,
dy = boundaryY - this.y,
dist = Math.sqrt(dx * dx + dy * dy);
this.radians = this.angle * Math.PI/ 180;
this.velX = Math.cos(this.radians) * this.speed;
this.velY = Math.sin(this.radians) * this.speed;
// check if we are still inside of our boundary.
if (dist < boundaryRadius-this.radius) {
this.x += this.velX;
this.y += this.velY;
} else {
// collision, step back and choose an opposite angle with a bit of randomness.
this.x -= this.velX;
this.y -= this.velY;
this.angle += 180+Math.random()*45;
}
};
Ball.prototype.render = function () {
ctx.fillStyle = this.color;
ctx.beginPath();
// draw our circle with x and y being the center
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.px, this.py);
ctx.closePath();
};
var balls = [],
ballNum = 10;
for(var i = 0; i < ballNum; i++){
balls.push(new Ball(boundaryX + Math.random()*30, boundaryY + Math.random() * 30, 5 + Math.random()*15));
}
function drawAngles() {
var d = 50; //start line at (10, 20), move 50px away at angle of 30 degrees
var angle = 80 * Math.PI / 180;
ctx.beginPath();
ctx.moveTo(300, 0);
ctx.lineTo(300, 600); //x, y
ctx.moveTo(0, 300);
ctx.lineTo(600, 300);
ctx.arc(boundaryX, boundaryY, boundaryRadius, 0, 2 * Math.PI);
ctx.strokeStyle = "#000";
ctx.stroke();
}
function render() {
ctx.clearRect(0, 0, width, height);
drawAngles();
balls.forEach(function(e){
e.update();
e.render();
});
requestAnimationFrame(render);
}
render();
Have you looked into ray casting? Also a neat trick for collision detection is to create a new hidden canvas used for collision detection only. You can then draw the circle on to it using only black and white. If the canvas is filled black with a white circle on it you can test collisions by checking the color of a specific pixel at point x. If point x is black the object has collided, if not it hasn't.

Canvas Arc not clearing

So I'm trying to make a rotating arc on a transparent background for a site. Most of it works, except for some reason each iteration of my arc drawing function also draws each previous arc.
Here is the code, and be careful clicking the link because this can lock-up your browser if you stay on the page for too long (only confirmed to do that in Firefox).
http://jsfiddle.net/djaEq/3/
This is the JavaScript:
function MovingArc(cx, cy, radius, size, angle){
this.centerX = cx;
this.centerY = cy;
this.radius = radius; //thickness
this.size = size; //width
this.angle = angle;
this.startAngle = 0;
this.endAngle = 0;
this.checkClockwise = function(){
var num = Math.round(Math.random());
return (num == 1);
}
this.clockwise = this.checkClockwise();
this.update = function(){
if(this.clockwise) this.angle += 2;
else this.angle -= 2;
this.startAngle = this.angle - (this.size/2);
this.endAngle = this.angle + (this.size/2);
}
}
var myArc;
function init() {
myArc = new MovingArc(50,50, 50,50, 60);
update();
setInterval(update, 1000 / 60);
}
function drawCanvas(){
var ctx = $('#canvasbanner')[0].getContext("2d"); //get context of canvas
ctx.clearRect(0,0,400,400);
ctx.fillStyle = "#000000";;
ctx.arc(myArc.centerX,myArc.centerY,myArc.radius,myArc.startAngle,myArc.endAngle,false);
ctx.stroke();
}
function update(){
myArc.update();
drawCanvas();
}
$(document).ready(function() {
console.log("doc ready");
init();
});
The arc command takes radians for starting and ending angles and it looks like you're sending degree angles.
Make sure to ctx.beginPath() or else you're redrawing every previously draw arc with each iteration.
function drawCanvas(){
var ctx = $('#canvasbanner')[0].getContext("2d"); //get context of canvas
ctx.clearRect(0,0,400,400);
ctx.beginPath();
ctx.fillStyle = "#000000";;
ctx.arc(myArc.centerX,myArc.centerY,myArc.radius,myArc.startAngle,myArc.endAngle,false);
ctx.stroke();
}

Categories

Resources