简体   繁体   中英

Ball bouncing in different sized canvases javascript

So for a project I'm working on, I would like four different sized canvases with four balls bouncing off the walls at the same speed. The code works fine when all the canvases are the same size, but as soon as I try and adjust the canvases heights and widths, the ball continues to bounce in an area the size of the first canvas. I'm not sure what I'm doing wrong, and I would really appreciate some help. I'm fairly new to javascript.

Here is the HTML:

<canvas id="canvas1" width="200" height="200"></canvas>
<canvas id="canvas2" width="200" height="400"></canvas>
<canvas id="canvas3" width="400" height="200"></canvas>
<canvas id="canvas4" width="200" height="200"></canvas>

And here is the Javascript:

var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
var x = canvas.width/2;
var y = canvas.height-30;
var dx = -2;
var dy = 2;
var ballRadius = 10;

var canvas2 = document.getElementById("canvas2");
var ctx2 = canvas2.getContext("2d");
var x2 = canvas2.width/2;
var y2 = canvas2.height-30;

var canvas3 = document.getElementById("canvas3");
var ctx3 = canvas3.getContext("2d");
var x3 = canvas3.width/2;
var y3 = canvas3.height-30;

var canvas4 = document.getElementById("canvas4");
var ctx4 = canvas4.getContext("2d");
var x4 = canvas4.width/2;
var y4 = canvas4.height-30;

function drawBall() {
    ctx.beginPath();
    ctx.arc(x, y, ballRadius, 0, Math.PI*2);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();

    ctx2.beginPath();
    ctx2.arc(x2, y2, ballRadius, 0, Math.PI*2);
    ctx2.fillStyle = "#0095DD";
    ctx2.fill();
    ctx2.closePath();

    ctx3.beginPath();
    ctx3.arc(x3, y3, ballRadius, 0, Math.PI*2);
    ctx3.fillStyle = "#0095DD";
    ctx3.fill();
    ctx3.closePath();

    ctx4.beginPath();
    ctx4.arc(x4, y4, ballRadius, 0, Math.PI*2);
    ctx4.fillStyle = "#0095DD";
    ctx4.fill();
    ctx4.closePath();
}

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
    ctx3.clearRect(0, 0, canvas3.width, canvas3.height);
    ctx4.clearRect(0, 0, canvas4.width, canvas4.height);

    drawBall();
    x += dx;
    y += dy;
    x2 += dx;
    y2 += dy;
    x3 += dx;
    y3 += dy;
    x4 += dx;
    y4 += dy;

    if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) {
        dy = -dy; }
    if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) {
        dx = -dx; }

    if(y2 + dy > canvas2.height-ballRadius || y2 + dy < ballRadius) {
        dy = -dy; }
    if(x2 + dx > canvas2.width-ballRadius || x2 + dx < ballRadius) {
        dx = -dx; }

    if(y3 + dy > canvas3.height-ballRadius || y3 + dy < ballRadius) {
        dy = -dy; }
    if(x3 + dx > canvas3.width-ballRadius || x3 + dx < ballRadius) {
        dx = -dx; }

    if(y4 + dy > canvas4.height-ballRadius || y4 + dy < ballRadius) {
        dy = -dy; }
    if(x4 + dx > canvas4.width-ballRadius || x4 + dx < ballRadius) {
        dx = -dx; }
    }
    setInterval(draw, 10);

The problem is that all of the balls share the same velocity (dx,dy), so when one of the balls hits a wall it causes all of them to bounce. To fix it you'd need to have a velocity for each individual ball (dx2,dy2,etc)

In your code the balls share the same velocity values (dx,dy) which will result in all balls behaving the same way.

One way of solving your issue and make your code cleaner (/scalable) is to create a class for handling the drawing, and different instances of that class for each canvas, ensuring canvas x,y, velocity and other variables are separate

I forked a fiddle from @JupeP where I create a base class and new instances for each canvas by passing the individual Canvas id.

http://jsfiddle.net/jtjpv2bf/1/

<canvas id="myCanvas" width="800" height="400" style="border:1px solid #000000;"></canvas>
<canvas id="myCanvas1" width="400" height="400" style="border:1px solid #000000;"></canvas>
<canvas id="myCanvas2" width="200" height="200" style="border:1px solid #000000;"></canvas>
<canvas id="myCanvas3" width="600" height="300" style="border:1px solid #000000;"></canvas>

window.requestAnimFrame =
  window.requestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  function(callback) {
    window.setTimeout(callback, 1000 / 60);
  };

AnimationHandler = function(canvasId) {

  var canvas, WIDTH, HEIGHT, 
      ctx, dx, dy, x, y;

  function init() {
    // find the <canvas> element
    canvas = document.getElementById(canvasId);
    WIDTH = canvas.width;
    HEIGHT = canvas.height;
    // get canvas context
    ctx = canvas.getContext("2d");
    (...)
    }

  AnimationHandler.prototype = {}
  AnimationHandler.prototype.constructor = init;
  ...
  return init();
};

new AnimationHandler("myCanvas");
new AnimationHandler("myCanvas1");
new AnimationHandler("myCanvas2");
new AnimationHandler("myCanvas3");

RequestAnimationFrame

Also, consider using requestAnimationFrame instead of setInterval. It's an optimized API that browsers use for concurrent animations and will pause if not in an active tab (saves battery/ improves perfomance).

Paul Irish explains it here

EDIT
my code doesn't work, but you can still take the idea of proportions with the values and use a different variable for each dx and dy


Instead of using dx and dy for the speed of each ball, use corresponding numbers for canvas and ball:

// dx1, dx2, dx3, dx4, dy1, dy2, dy3, dy4 instead of dx, dy
// also you can use proportions to keep them bouncing at the same time
// see below

var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
var x = canvas.width/2;
var y = canvas.height/2;
/*var dx = -2;
var dy = 2;*/
var ballRadius = 10;

var canvas2 = document.getElementById("canvas2");
var ctx2 = canvas2.getContext("2d");
var x2 = canvas2.width/2;
var y2 = canvas2.height/2;

var canvas3 = document.getElementById("canvas3");
var ctx3 = canvas3.getContext("2d");
var x3 = canvas3.width/2;
var y3 = canvas3.height/2;

var canvas4 = document.getElementById("canvas4");
var ctx4 = canvas4.getContext("2d");
var x4 = canvas4.width/2;
var y4 = canvas4.height/2;

/***********************************************/
/***********************************************/
/***********************************************/
/***********************************************/
/***********************************************/

var dx1 = 2, dx2 = dx1*canvas2.width/canvas.width, dx3 = dx1*canvas3.width/canvas.width, dx4 = dx1*canvas4.width/canvas.width;
var dy1 = 2, dy2 = dy1*canvas2.height/canvas.height, dy3 = dy1*canvas3.height/canvas.height, dy4 = dy1*canvas4.height/canvas.height;

function drawBalls() {
    ctx.beginPath();
    ctx.arc(x, y, ballRadius, 0, Math.PI*2);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();

    ctx2.beginPath();
    ctx2.arc(x2, y2, ballRadius, 0, Math.PI*2);
    ctx2.fillStyle = "#0095DD";
    ctx2.fill();
    ctx2.closePath();

    ctx3.beginPath();
    ctx3.arc(x3, y3, ballRadius, 0, Math.PI*2);
    ctx3.fillStyle = "#0095DD";
    ctx3.fill();
    ctx3.closePath();

    ctx4.beginPath();
    ctx4.arc(x4, y4, ballRadius, 0, Math.PI*2);
    ctx4.fillStyle = "#0095DD";
    ctx4.fill();
    ctx4.closePath();
}

function clearCanvases() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
    ctx3.clearRect(0, 0, canvas3.width, canvas3.height);
    ctx4.clearRect(0, 0, canvas4.width, canvas4.height);
}

function changeSpeeds() {
    x += dx1;
    y += dy1;
    x2 += dx2;
    y2 += dy2;
    x3 += dx3;
    y3 += dy3;
    x4 += dx4;
    y4 += dy4;
}

function what(a, b, c, r)
{
    if (a + b > c - r || a + b < r)
    {
        b = -b;
    }
}

function checkForCollisions() {
    what(y, dy1, canvas.height, ballRadius);
    what(x, dx1, canvas.width, ballRadius);
    what(y2, dy2, canvas2.height, ballRadius);
    what(x2, dx2, canvas2.width, ballRadius);
    what(y3, dy3, canvas3.height, ballRadius);
    what(x3, dx3, canvas3.width, ballRadius);
    what(y4, dy4, canvas4.height, ballRadius);
    what(x4, dx4, canvas4.width, ballRadius);
}


// I split the draw function into four functions, named bounceBalls
function bounceBalls()
{
    clearCanvases();  // context.clearRect for each of the four canvases...
    drawBalls();    // drawBall in your code
    changeSpeeds();
    checkForCollisions();    // bouncing against sides
}

var FPS = 60;
window.setInterval(bounceBalls, 1000/FPS);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM