Animating shapes in javascript - javascript

So I'm trying to have shapes animate across the canvas and then bounce back after hitting the edge, and I'm unclear why my other two shapes do not have velocity like my ball does. I'll post the code below.
<html>
<body>
<section>
<div>
<canvas id="canvas" width="700" height="300"></canvas>
</div>
<script type="text/javascript">
var canvas,
context,
x = Math.floor((Math.random() * 400) + 1), //Ball x coordinate
y = Math.floor((Math.random() * 300) + 1), //Ball y coordinate
dx = Math.floor((Math.random() * 10) + 1), //X velocity
dy = Math.floor((Math.random() * 4) + 1), //Y velocity
width = 700, //Width of the background
height = 300; //Height of the background
function drawCircle(x,y,r) { //Draws the ball
context.beginPath();
context.arc(x, y, r, 0, Math.PI*2, true);
context.fill();
}
function drawRect(x,y,w,h) { //Draws the background
context.beginPath();
context.rect(x,y,w,h);
context.closePath();
context.fill();
}
function start() { //Runs when the window loads. Stores canvas and context in variables. Runs "draw" on an interval of 10ms
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
return setInterval(draw, 10);
}
function drawSquare(){
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.fillStyle = "white";
ctx.beginPath();
ctx.moveTo(200,50);
ctx.lineTo(250,50);
ctx.lineTo(300, 100);
ctx.lineTo(250,25);
ctx.fill();
}
}
function drawTri(){
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(75,25);
ctx.fill();
}
}
function draw() {
context.clearRect(0, 0, width, height); //Clears the drawing space
context.fillStyle = "black"; //Sets fillstyle to black (for the background)
drawRect(0,0, width, height); //Draws the background
context.fillStyle = "white"; //Sets fillstyle to white (for the ball)
drawCircle(x, y, 10);
drawTri();
drawSquare(); //Calls function to draw the ball
if (x + dx > width || x + dx < 0) //If the ball would leave the width (right or left) on the next redraw...
dx = -dx; //reverse the ball's velocity
if (y + dy > height || y + dy < 0) //If the ball would leave the height (top or bottom) on the next redraw...
dy = -dy; //reverse the ball's velocity
x += dx; //Increase ball x speed by velocity
y += dy; //Increase ball y speed by velocity
}
window.onload = start; //Run "start" function after the window loads
</script>
</section>
</body>
</html>

you forgot to do the drawing with the variables. And the variables are the same (as in the 2 shapes will always be together) because if x is 150 for the circle, the rect will be behind it. So, you need more variables to keep track of it like this: circ1x,circ1y,circ2x,circ2y,rect1x,rect1y,rect2x,rect2y

Your ball is being drawn using variable coordinates (x & y)
context.arc(x, y, r, 0, Math.PI*2, true);
Your shapes are being drawn using hardcoded, constant values:
ctx.moveTo(200,50);
ctx.lineTo(250,50);
ctx.lineTo(300, 100);
ctx.lineTo(250,25);
&
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(75,25);
To animate them, use variables to draw the other shapes, and update those variables. eg.
var x = 75,
y = 50;
ctx.moveTo(x,y);
ctx.lineTo(x + 25, y + 25);
ctx.lineTo(x, y - 25);

Related

How to make an object blink once in js?

I'm not proficient in js to say the very least.
I have a bouncy ball in a container in js. When the ball hits a certain part of its container, I want a box to light up briefly. I want to control the duration of the blink. I want the blink to happen once.
So far this works but the blink is incredibly brief:
I have a canvas object
<Canvas id="button1" width="100px" height="50px"></canvas>
And this code in place. Let's assume for simplicity's sake that the ball being in the right place sets ball_is_in_area to T for a very short duration.
var button1;
var button1ctx;
var ball_is_in_area = F;
button1 = document.getElementById("button1");
button1ctx = button1.getContext("2d");
button1ctx.fillStyle = "white";
button1ctx.fillRect(0, 0, canvas.width, canvas.height);
if( ball_is_in_area == T){
snd.play();
button1ctx.fillStyle = "red";
button1ctx.fillRect(0, 0, canvas.width, canvas.height);
}
"Don't use a canvas object use X" type answers are also appreciated!
Using requestAnimationFrame and based on this sample, you can use the timestamp parameter provided and, whenever you hit a wall, extend the timer for when the blink will end. Then use this timer to determine the color of the background.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var x = canvas.width / 2, y = canvas.height / 2;
var ballRadius = 10, dx = 2, dy = -2;
var blinkDuration = 60; // Duration in milliseconds
var endBlink = 0;
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw(timeStamp) {
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
endBlink = timeStamp + blinkDuration; // Hit a wall
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
endBlink = timeStamp + blinkDuration;
dy = -dy;
}
// If endBlink > timeStamp, change the color
ctx.fillStyle = (endBlink > timeStamp ? "#338" : "#114");
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawBall();
x += dx;
y += dy;
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
body { margin: 0 }
<canvas id="canvas" width="630" height="190"></canvas>

Ball is bouncing from the centre not the edge

I have coded a ball to bounce around on a canvas, however, the ball only bounces from the centre, not the edge.
I have already tried putting the radius instead of the x and y but that didn't help at all, as of right now I am out of ideas.
I expect the ball to bounce of the edges of itself rather than the centre of it
<!DOCTYPE>
<html>
<head>
<title>Ball Bounce</title>
<style>
canvas {
border-width: 5px;
border-style: ridge;
border-color: #FF0000;
}
</style>
</head>
<body>
<canvas id="testCanvas" width="700" height="400"></canvas>
<script>
var canvas = document.getElementById("testCanvas");
var ctx = canvas.getContext("2d");
var x = 300;
var y = 300;
var radius = 50;
var circleColour = "green";
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var dx = 2;
var dy = -2;
function circle(x, y, r, c) {
ctx.beginPath();
ctx.arc(x, y, r, 0, 2 * Math.PI, false);
ctx.fillStyle = c;
ctx.fill()
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
circle(x, y, radius, circleColour)
if (y<0 || y > canvasHeight){
dy = -dy;
}
if (x>canvasWidth || x<0){
dx = -dx;
}
x = x + dx;
y = y + dy;
}
setInterval(draw, 5.5);
</script>
</body>
</html>
I have already tried putting the radius instead of the x and y but that didn't help at all...
You need to combine the radius with x and y (or the canvas edges) to make sure the ball changes direction at all four borders.
if (y - radius < 0 || y + radius > canvasHeight) {
dy = -dy;
}
if (x + radius > canvasWidth || x - radius < 0) {
dx = -dx;
}

How to use RequestAnimationFrame correctly?

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));
}
};

Drawing a angle / arc, filled with a radiant in canvas?

Problem: Im drawing a spaceship on the canvas. Upon hovering over it's x/y, im drawing an arc on the canvas, indicating the starships weapons angle and range (considering the starships current Baring/facing). Currently the determined angle is being drawn in green and extends as far as the weapons range value allows.
However, i would like to use a gradiant to fill the determined arc to indicate a drop-off in accuracy (i.e. gradiant begins at green, moves to orange, turns red the further away from the starships Position the angle is).
However, i dont know how i could replace my stock ctx.fill() on the drawn arc with a gradiant.
var ship {
loc: {x, y}, // lets say 100, 100
facing: facing // lets say facing 0, i.e. straight right
weapons: objects (range, startArc, endArc) // lets say 50, 300, 60 -> 120 degree angle, so -60 and +60 from facing (0/360)
}
for (var i = 0; i < weapon.arc.length; i++){
var p1 = getPointInDirection(weapon.range, weapon.arc[i][0] + angle, pos.x, pos.y);
var p2 = getPointInDirection(weapon.range, weapon.arc[i][1] + angle, pos.x, pos.y)
var dist = getDistance( {x: pos.x, y: pos.y}, p1);
var rad1 = degreeToRadian(weapon.arc[i][0] + angle);
var rad2 = degreeToRadian(weapon.arc[i][1] + angle);
fxCtx.beginPath();
fxCtx.moveTo(pos.x, pos.y);
fxCtx.lineTo(p1.x, p1.y);
fxCtx.arc(pos.x, pos.y, dist, rad1, rad2, false);
fxCtx.closePath();
fxCtx.globalAlpha = 0.3;
fxCtx.fillStyle = "green";
fxCtx.fill();
fxCtx.globalAlpha = 1;
}
is it possible to replace the arc/globalalpha/fill so use a gradiant flow instead of it being colored fixed and if so, how ?
thanks
To fill an arc with a gradient, animated just for the fun.
Uses a radial gradient and set colour stops as a fraction of distance.
The function createRadialGradient takes 6 numbers the position x,y and start radius and the position x,y and end radius of the gradient.
Colour stops are added via the gradient object addColorStop function that takes a value 0 inner to 1 outer part of the gradient and the colour as a CSS color string. "#F00" or "rgba(200,0,0,0.5)" or "RED"
Then just use the gradient as the fill style.
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
function update(time) {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// position of zones in fractions
var posRed = 0.8 + Math.sin(time / 100) * 0.091;
var posOrange = 0.5 + Math.sin(time / 200) * 0.2;
var posGreen = 0.1 + Math.sin(time / 300) * 0.1;
var pos = {
x: canvas.width / 2,
y: canvas.height / 2
};
var dist = 100;
var ang1 = 2 + Math.sin(time / 1000) * 0.5;
var ang2 = 4 + Math.sin(time / 1300) * 0.5;
var grad = ctx.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, dist);
grad.addColorStop(0, "#0A0");
grad.addColorStop(posGreen, "#0A0");
grad.addColorStop(posOrange, "#F80");
grad.addColorStop(posRed, "#F00");
grad.addColorStop(1, "#000");
ctx.fillStyle = grad;
ctx.beginPath();
ctx.moveTo(pos.x, pos.y);
ctx.arc(pos.x, pos.y, dist, ang1, ang2);
ctx.fill();
requestAnimationFrame(update);
}
requestAnimationFrame(update);

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.

Categories

Resources