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>
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I just want a simple collision detection so these balls bounce off of each other and if you can also hit me up with a better way of coding these types of animations , ill be happy. Ive heard there is a better way for doing that with object oriented Javascript , but it seems complicated for me since im still a beginner. Please try to explain the code you suggest me since im still learning .
var canvas = document.getElementById("canv");
var ctx = canvas.getContext("2d");
var x = canvas.width/2;
var x2 = canvas.width/2;
var y = canvas.height-20;
var y2 = 20;
var ballRadius = 20;
var ballRadius2 = 20;
var dx = 2;
var dy = -2;
var dx2 = 2;
var dy2 = 2;
function drawBall(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(x, y,ballRadius, 0, Math.PI*2);
ctx.fillStyle = "green";
ctx.fill();
ctx.closePath();
}
function drawBall2(){
ctx.beginPath();
ctx.arc(x2, y2,ballRadius2, 0, Math.PI*2);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
function draw(){
drawBall();
drawBall2();
x += dx;
y += dy;
x2 += dx2;
y2 += dy2
if (x && x2 > canvas.width - ballRadius || x && x2 < ballRadius){
dx = -dx;
dx2 = -dx2;
}
if (y && y2 > canvas.height - ballRadius || y && y2 < 0){
dy = -dy;
dy2 = -dy2;
}
}
setInterval(draw, 10);
if you guys can help me simplifying this code too ill be thankful.
Great question! I wanted to say goodjob on diving into javascript and HTML5. Both of these are fantastic tools that will surely help you put together some awesome projects.
Now to the question. You are asking how to do a simple collision test between the two balls. I will explain this shortly, but before I do, I wanted to give you a reference on Javascript object oriented programming.
The collision test between the two balls is very similar to the collision test with the walls. The main difference is that you can't consider only the x or the y value alone, you must consider them both simultaneously.
With this in mind, the balls need to 'bounce' if the distance between them with respect to x and y is equal to or less than their combined radius.
Now all that is left is to convert that logical statement into a javascript logical statement. I have provided one potential solution, and urge you to make a better one if you are able to.
//test for 'hits' with eachother
if((Math.abs(ballOne.x - ballTwo.x) < (ballOne.radius + ballTwo.radius)) && (Math.abs(ballOne.y - ballTwo.y) < (ballOne.radius + ballTwo.radius)))
{
//reverse ball one
ballOne.dx = -ballOne.dx;
ballOne.dy = -ballOne.dy;
//reverse ball two
ballTwo.dx = -ballTwo.dx;
ballTwo.dy = -ballTwo.dy;
}
I have also provided what I would consider a 'simpler' and certainly easier solution to your current project. I suggest reading through this and utilizing the documents at the link above to better understand Javascript OOP.
Goodluck, and Happy coding!
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<canvas id="canv" width="400" height="400"></canvas>
<script>
//define canvas object
var canvas = document.getElementById("canv");
var ctx = canvas.getContext("2d");
//define ball objects
var ballOne;
var ballTwo;
//javascript 'constructor function' for ball
function Ball(x, y, dx, dy, radius, color){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.color = color;
}
function wallCollisionDetection(balls){
//for loop to 'go through' the array of balls
var i;
for(i = 0; i < balls.length; i++){
//collision test between a ball and a wall
//x-direction tests
if((balls[i].x + balls[i].radius >= canvas.width) || (balls[i].x - balls[i].radius <= 0) ){ balls[i].dx = -balls[i].dx; }
//y-direction tests
if((balls[i].y + balls[i].radius >= canvas.height) || (balls[i].y - balls[i].radius <= 0) ){ balls[i].dy = -balls[i].dy; }
}
}
function updateBall(ball){
ball.x += ball.dx;
ball.y += ball.dy;
}
function drawBall(ball){
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.closePath();
}
function draw(){
//draw both ball objects
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall(ballOne);
drawBall(ballTwo);
//update the balls' x & y based on the balls' dx & dy
updateBall(ballOne);
updateBall(ballTwo);
//test for 'hits' with the walls
wallCollisionDetection([ballOne, ballTwo]);
//test for 'hits' with eachother
if((Math.abs(ballOne.x - ballTwo.x) < (ballOne.radius + ballTwo.radius)) && (Math.abs(ballOne.y - ballTwo.y) < (ballOne.radius + ballTwo.radius)))
{
//reverse ball one
ballOne.dx = -ballOne.dx;
ballOne.dy = -ballOne.dy;
//reverse ball two
ballTwo.dx = -ballTwo.dx;
ballTwo.dy = -ballTwo.dy;
}
}
function initializeBalls(){
//set-up ball objects
ballOne = new Ball(canvas.width / 2, canvas.height - 50, 2, -2, 15, "green");
ballTwo = new Ball(canvas.width / 2, 40, 3, 2, 35, "blue");
}
initializeBalls();
setInterval(draw, 10);
</script>
</body>
</html>
I currently have a rotating bouncing ball within an html5 canvas and I am looking to insert an SVG image inside the ball that moves and rotates with it
I have this code from researching this but unsure if this is correct
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
}
img.src = "";
Does anyone have any suggestion on how I might achieve this?
Here is my code
<canvas id="myCanvas"></canvas>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
// SPEED
var dx = 4;
var dy = -4;
var radius = 120;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = "#9370DB";
ctx.fill();
ctx.closePath();
if (x + dx > canvas.width - radius) {
dx = -dx;
}
if (x + dx < radius) {
dx = -dx;
}
if (y + dy > canvas.height - radius) {
dy = -dy;
}
if (y + dy < radius) {
dy = -dy;
}
x += dx;
y += dy;
}
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
x = canvas.width / 2;
y = canvas.height / 2;
setInterval(draw, 10);
var img = new Image();
img.src = ""; // Put the path to you SVG image here.
img.onload = function() {
ctx.drawImage(img, 0, 0);
}
This should work
One way of doing it would be putting the image hidden in the HTML. In this case the image is an svg as data uri and has an id="apple" and you can say:
var img = apple;
To draw the image inside the ball you need to use the center of the ball, for example like this:
ctx.drawImage(img, x-img.width/2,y-img.height/2)
Also instead of using setInterval I'm using requestAnimationFrame and the image is not getting out of the screen on resize. I hope you will find my answer useful.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var rid = null;// request animation id
// SPEED
var dx = 4;
var dy = -4;
var radius = 120;
var img = apple;// the image is the one with the id="apple"
function draw() {
rid = window.requestAnimationFrame(draw);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = "#9370DB";
ctx.fill();
ctx.closePath();
//draw the image in the center of the ball
ctx.drawImage(img, x-img.width/2,y-img.height/2)
if (x + dx > canvas.width - radius) {
dx = -dx;
}
if (x + dx < radius) {
dx = -dx;
}
if (y + dy > canvas.height - radius) {
dy = -dy;
}
if (y + dy < radius) {
dy = -dy;
}
x += dx;
y += dy;
}
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
//stop the animation
if(rid){window.cancelAnimationFrame(rid); rid= null;}
//get the size of the canvas
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
x = canvas.width / 2;
y = canvas.height / 2;
//restart the animation
draw()
}
window.setTimeout(function() {
resizeCanvas();
window.addEventListener('resize', resizeCanvas, false);
}, 15);
<canvas id="myCanvas">
<img id="apple" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='Layer_1' x='0px' y='0px' width='106px' height='122px' viewBox='41 54 106 122'%3E%3Cg%3E%3Cpath fill='%23FFFFFF' stroke='%23ED1D24' stroke-width='2' stroke-miterlimit='10' d='M143.099,93.757c0,0-14.173,8.549-13.724,23.173 c0.449,14.624,11.954,23.413,15.974,24.073c1.569,0.258-9.245,22.049-15.984,27.448c-6.74,5.4-13.714,6.524-24.513,2.25c-10.8-4.275-18.449,0.275-24.749,2.612c-6.299,2.337-13.949-0.137-24.298-14.987c-10.349-14.849-21.823-49.271-6.074-66.146c15.749-16.874,33.298-10.124,38.022-7.875c4.725,2.25,13.05,2.025,22.499-2.25C119.7,77.782,138.374,86.782,143.099,93.757z'/%3E%3C/g%3E%3Cg%3E%3Cpath fill='%23FFFFFF' stroke='%23ED1D24' stroke-width='2' stroke-miterlimit='10' d='M118.575,54.609c0,0,0.9,5.625-1.35,10.349 s-10.718,20.936-22.994,17.999c-0.308-0.073-2.102-5.506,0.532-11.027C98.48,64.138,108.171,55.399,118.575,54.609z'/%3E%3C/g%3E%3C/svg%3E" />
</canvas>
Please run the code on full page.
I need to have my ball jump again when I click, it, but I don't know how to put my functions together to make it do a jump again. Can anyone please help me with this? I repeat, I need the ball to jump agin when I click, and it needs to be able to do this in the air, no bounce needed.
var canvas, ctx, container;
canvas = document.createElement('canvas');
ctx = canvas.getContext("2d");
var ball;
var vy;
var gravity = 0.5;
var bounce = 0.7;
var xFriction = 0.1;
function init() {
setupCanvas();
vy = (Math.random() * -15) + -5;
ball = {
x: canvas.width / 2,
y: 100,
radius: 20,
status: 0,
color: "red"
};
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2, false);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.closePath()
ballMovement();
}
setInterval(draw, 1000 / 35);
function ballMovement() {
ball.y += vy;
vy += gravity;
if (ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
vx *= -1;
}
if (ball.y + ball.radius > canvas.height) {
ball.y = canvas.height - ball.radius;
vy *= -bounce;
vy = 0;
if (Math.abs(vx) < 1.1)
vx = 0;
xF();
}
}
function setupCanvas() { //setup canvas
container = document.createElement('div');
container.className = "container";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(container);
container.appendChild(canvas);
ctx.strokeStyle = "#ffffff";
ctx.lineWidth = 2;
}
I need it to be able to jump in the air, and it to be able to fall down a gain to a halting stop.
I am working on drawing moving rectangles on my canvas. I made a template function for the test purpose and it works, but since i want to draw more of the rectangles with same animation effect I have to make this template function comes to the constructor, getContextand now the problem occurs:
the template function:
ctx = getContext('2d');
var boxHeight = canvas.height/40;
var boxWidth = canvas.width/20;
function drawBox(){
var x = 20;
var y = canvas.height;
var w = boxWidth;
var h = boxHeight;
var timer = 0;
var ladder = Math.floor(Math.random()*((canvas.height*0.5)/boxHeight)) + 1;
for(var i = 0; i < ladder; i++){
ctx.fillStyle = 'hsl('+Math.abs(Math.sin(timer) * 255)+', 40%, 50%)';
ctx.fillRect(x,y,w,h);
ctx.strokeRect(x,y,w,h);
ctx.lineWidth = 2;
ctx.stroke();
y -= boxHeight;
timer += Math.random()*0.3;
}
}
function animate(){
ctx.clearRect(0,0,canvas.width,canvas.height);
window.requestAnimationFrame(animate);
drawBox();
}
animate();
this template function drawBox()just working fine, then i tried to enclose its properties into a Box()constructor object:
function Box(x, width) {
this.postion = {
x: x,
y: canvas.height
};
this.width = width;
this.height = canvas.height / 40;
this.colorTimer = 0;
this.draw = function() {
this.colorTimer += Math.random() * 0.3;
var ladder = Math.floor(Math.random() * ((canvas.height * 0.5) / boxHeight)) + 1;
for (var i = 0; i < ladder; i++) {
ctx.fillStyle = 'hsl(' + Math.abs(Math.sin(this.colorTimer) * 255) + ', 40%, 50%)';
ctx.fillRect(this.postion.x, this.postion.y, this.width, this.height);
ctx.strokeRect(this.postion.x, this.postion.y, this.width, this.height);
ctx.lineWidth = 2;
ctx.stroke();
this.postion.y -= this.height;
}
}
}
var myBox = new Box(20, boxWidth);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
window.requestAnimationFrame(animate);
myBox.draw();
}
animate();
this is not working, i have been stuck with this about 2 hours and i don't think there is any method or properties difference between my Boxconstructor and my drawBoxobject. When it comes to myBoxobject to calling its draw()method, there is nothing pop out on the screen.
I am wondering did i just miss something important when creating Boxconstructor object? Could someone give me a hint please?
As #Todesengel mentioned, the real issue here is, you are re-initializing all the variables each time the template function (drawBox) is called. But you are not doing the same for the constructor. To resolve this, put this.colorTimer = 0 and this.postion.y = canvas.height insde the draw method (as these are the variables that need to be re-initialized).
However, there are other issues :
you are increasing the timer variable inside the for loop, in template function, but not doing the same for constructor
as #Barmar mentioned, you should define draw method as Box.prototype.draw, for efficiency (not mandatory though)
Here is the revised version of your code :
ctx = canvas.getContext('2d');
var boxHeight = canvas.height / 40;
var boxWidth = canvas.width / 20;
function Box(x, width) {
this.postion = {
x: x,
y: canvas.height
};
this.width = width;
this.height = canvas.height / 40;
}
Box.prototype.draw = function() {
this.colorTimer = 0;
this.postion.y = canvas.height;
var ladder = Math.floor(Math.random() * ((canvas.height * 0.5) / this.height)) + 1;
for (var i = 0; i < ladder; i++) {
ctx.fillStyle = 'hsl(' + Math.abs(Math.sin(this.colorTimer) * 255) + ', 40%, 50%)';
ctx.fillRect(this.postion.x, this.postion.y, this.width, this.height);
ctx.strokeRect(this.postion.x, this.postion.y, this.width, this.height);
ctx.lineWidth = 2;
ctx.stroke();
this.postion.y -= this.height;
this.colorTimer += Math.random() * 0.3;
}
}
var myBox = new Box(20, boxWidth);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
myBox.draw();
window.requestAnimationFrame(animate);
}
animate();
<canvas id="canvas" width="300" height="300"></canvas>
I believe the important thing to note here is that in your first case, there is a new drawBox function being called every time, with the variables being instantiated and initialized, or "reset", each time. In your second case, the myBox object is not being recreated each time, so you have left over variables. These will not behave the same way. It should work as expected if you move var myBox = new Box(20, boxWidth); into the animate function.
Another fix, if you don't want to do recreate the myBox object for each call, is to reset the left over variables after each animate call. It would be more efficient, and probably more desirable, to do it this way.
You should not modify this.position.y in the draw method.
So remove this assignment from the loop:
this.postion.y -= this.height;
... and change the following lines to dynamically add -i*this.height:
ctx.fillRect(this.postion.x, this.postion.y-i*this.height, this.width, this.height);
ctx.strokeRect(this.postion.x, this.postion.y-i*this.height, this.width, this.height);
As others have said, you should better define the method on the prototype. And colorTimer should change in the for loop. I think you could do with a local variable though.
Demo:
var ctx = canvas.getContext('2d');
var boxHeight = canvas.height/40;
var boxWidth = canvas.width/20;
function Box(x, width) {
this.postion = {
x: x,
y: canvas.height
};
this.width = width;
this.height = canvas.height / 40;
}
Box.prototype.draw = function() {
var ladder = Math.floor(Math.random() * ((canvas.height * 0.5) / boxHeight)) + 1;
var colorTimer = 0;
for (var i = 0; i < ladder; i++) {
ctx.fillStyle = 'hsl(' + Math.abs(Math.sin(colorTimer) * 255) + ', 40%, 50%)';
ctx.fillRect(this.postion.x, this.postion.y-i*this.height, this.width, this.height);
ctx.strokeRect(this.postion.x, this.postion.y-i*this.height, this.width, this.height);
ctx.lineWidth = 2;
ctx.stroke();
colorTimer += Math.random() * 0.3;
}
};
var myBox = new Box(20, boxWidth);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
window.requestAnimationFrame(animate);
myBox.draw();
}
animate();
<canvas id="canvas"></canvas>
I'm quite new to JS and have gone through courses online but, very frustratingly, I just seem to have such a hard time on my own so I'm sorry if this question has an obvious answer. Basically, this program bounces a colored ball around within a box. I want that color to change every time it hits a wall. I figured out a way to do so by putting all information under one function but the tutorial I'm using is saying (for tidy code purposes) that 2 functions will be better and so I really just want to understand how to do what I want to do when info is available in different functions since I know I will have to do that in the future. I will comment important code lines. Thank you so much to anyone who can help.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var x = canvas.width/2;
var y = canvas.height-30;
var dx = 4;
var dy = -4;
var ballRadius = 30;
function drawBall() { \\draws the ball
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = "#ff0000";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
x += dx;
y += dy;
if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) { \\says when to bounce
dx = -dx;
drawBall.ctx.fillStyle = "#ff0000"; \\this line and next line are lines I wrote
drawBall.ctx.fill(); \\that are obviously incorrect (same goes for
} \\ if statement below). What am I doing wrong?
if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) {
dy = -dy;
drawBall.ctx.fillStyle = "#0095DD";
drawBall.ctx.fill();
}
}
setInterval(draw, 10);
what you can do is pass parameters that will alter the behavior of the function.
in this case you will be passing the color you want.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var x = canvas.width/2;
var y = canvas.height-30;
var dx = 4;
var dy = -4;
var ballRadius = 30;
function drawBall(color) { // draws the ball
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
x += dx;
y += dy;
if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) { // says when to bounce
dx = -dx;
drawBall("#ff0000");
}
if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) {
dy = -dy;
drawBall("#0095DD");
}
}
It seems that you mix some concepts of JavaScript. So for reasons of readability and design, I would create a 'class' for the ball. Something like this:
function Ball(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
}
You can create an instance of your ball with this:
var ball = new Ball(x, y, radius, color);
and access the properties in Java-style:
ball.color = "#0095DD";
You can also add some methods to your ball:
function Ball(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.draw = function(ctx) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
}
You can extend your code with this class and code. I think, you get it.