简体   繁体   中英

HTML5 Canvas | Bouncing Balls | Looping through an image array and placing different background images on each ball, centered

I am having a bit of a nightmare applying different background images onto balls that bounce around in the canvas under the effect of gravity and the bounds of my canvas, eventually settling down and stacking at the bottom.

I have created an image array, that I am trying to loop through and create a ball for each image, where the image becomes a centred background.

I can centre the image, but this makes the balls appear to leave the bounds of my canvas. So have reverted that change.

I am unable to get the background image to be different from the previous ball. Where the image shown on all balls is the last image in the array. However the number of balls created does reflect the number of images in the array.

Here is a link to a codepen: https://codepen.io/jason-is-my-name/pen/BbNRXB

html, body{
    width:100%;
    height:100%;
    margin: 0;
    padding: 0;
    background: #333333;
}
*{
    margin: 0;
    padding: 0;
}
    .container {
        width: 410px;
        height: 540px;
    }
    #ball-stage{
        width: 100%;
        height: 100%;
    }

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="container">
    <canvas id="ball-stage" ></canvas>
</div>

<script>
    /*
    /*
     * Created by frontside.com.au
     * Amended by Jason
    */

    $(document).ready(function () {
        $.ajaxSetup({
            cache: true
        });
        var url1 = "https://code.createjs.com/easeljs-0.6.0.min.js";
        $.getScript(url1, function () {
            new App();
        })
    });

    function App() {

        var self = this;
        self.running = false;
        self.initialized = false;
        var stageClicked = false;
        var stage, canvas;

        var canvasWidth = 410;
        var canvasHeight = 540;
        var bounce = -0.75;
        var balls = [];
        var _gravityY = 1;
        var _gravityX = 0;
        var FPS = 30;
        var infoText, detailsText;
        var ballsInitalized = false;
        var iOS = navigator.userAgent.match(/(iPod|iPhone|iPad)/);

        self.initialize = function () {
            toggleListeners(true);
            self.initCanvas();
            self.initGame();
        };

        var toggleListeners = function (enable) {
            if (!enable) return;
        };

        self.refresh = function () {}

        self.initCanvas = function () {
            canvas = $("#ball-stage").get(0);
            stage = new createjs.Stage(canvas);

            window.addEventListener('resize', onStageResize, false);
            onStageResize();
            createjs.Touch.enable(stage);
            createjs.Ticker.addListener(tick);
            createjs.Ticker.setFPS(FPS);

            self.initialized = true;
        }

        self.initGame = function () {
            initBalls(canvasWidth, canvasHeight);
        }

        var onStageResize = function () {
            stage.canvas.width = canvasWidth;
            stage.canvas.height = canvasHeight;
        }

        var initBalls = function (stageX, stageY) {

            var imagesArray = ["img-1.png","img-2.png","img-3.png","img-4.png","img-5.png","img-6.png","img-7.png","img-8.png"];

            for (var i = 0; i < imagesArray.length; i++) {
                console.log(i);

                var imageArray = imagesArray[i];
                console.log(imageArray);

                setTimeout(function () {

                    var arrayImage = new Image();
                    console.log(arrayImage);

                    arrayImage.onload = function(){

                        addBall(arrayImage, stageX / 2, 0);

                    }

                    arrayImage.src = imageArray;

                }, i * 1000);

            }
        }

        var addBall = function (img, x, y) {
            console.log(img);
            var shape = new createjs.Shape();
            shape.id = balls.length;
            shape.radius = 51.25;
            shape.mass = shape.radius;
            shape.x = x;
            shape.y = y;
            shape.vx = rand(-3, 3);
            shape.vy = rand(-3, 3);

            var image = new Image();
            image.src = img;
            shape.graphics.beginBitmapFill(img,'repeat').drawCircle(0, 0, shape.radius);

            stage.addChild(shape);
            balls.push(shape);
        }

        var numBalls = function () {
            return balls.length;
        }
        var tick = function () {
            balls.forEach(move);
            for (var ballA, i = 0, len = numBalls() - 1; i < len; i++) {
                ballA = balls[i];
                for (var ballB, j = i + 1; j < numBalls(); j++) {
                    ballB = balls[j];
                    checkCollision(ballA, ballB);
                }
            }

            stage.update();
        }

        var rotate = function (x, y, sin, cos, reverse) {
            return {
                x: (reverse) ? (x * cos + y * sin) : (x * cos - y * sin),
                y: (reverse) ? (y * cos - x * sin) : (y * cos + x * sin)
            };
        }

        var checkCollision = function (ball0, ball1) {
            var dx = ball1.x - ball0.x,
                dy = ball1.y - ball0.y,
                dist = Math.sqrt(dx * dx + dy * dy);
            //collision handling code here
            if (dist < ball0.radius + ball1.radius) {
                //calculate angle, sine, and cosine
                var angle = Math.atan2(dy, dx),
                    sin = Math.sin(angle),
                    cos = Math.cos(angle),
                    //rotate ball0's position
                    pos0 = {
                        x: 0,
                        y: 0
                    }, //point
                    //rotate ball1's position
                    pos1 = rotate(dx, dy, sin, cos, true),
                    //rotate ball0's velocity
                    vel0 = rotate(ball0.vx, ball0.vy, sin, cos, true),
                    //rotate ball1's velocity
                    vel1 = rotate(ball1.vx, ball1.vy, sin, cos, true),
                    //collision reaction
                    vxTotal = vel0.x - vel1.x;
                vel0.x = ((ball0.mass - ball1.mass) * vel0.x + 2 * ball1.mass * vel1.x) /
                    (ball0.mass + ball1.mass);
                vel1.x = vxTotal + vel0.x;
                //update position - to avoid objects becoming stuck together
                var absV = Math.abs(vel0.x) + Math.abs(vel1.x),
                    overlap = (ball0.radius + ball1.radius) - Math.abs(pos0.x - pos1.x);
                pos0.x += vel0.x / absV * overlap;
                pos1.x += vel1.x / absV * overlap;
                //rotate positions back
                var pos0F = rotate(pos0.x, pos0.y, sin, cos, false),
                    pos1F = rotate(pos1.x, pos1.y, sin, cos, false);
                //adjust positions to actual screen positions
                // ball1.x = ball0.x + pos1F.x;
                setBallX(ball1, ball0.x + pos1F.x)
                //ball1.y = ball0.y + pos1F.y;
                setBallY(ball1, ball0.y + pos1F.y)
                // ball0.x = ball0.x + pos0F.x;
                setBallX(ball0, ball0.x + pos0F.x)
                // ball0.y = ball0.y + pos0F.y;
                setBallY(ball0, ball0.y + pos0F.y)
                //rotate velocities back
                var vel0F = rotate(vel0.x, vel0.y, sin, cos, false),
                    vel1F = rotate(vel1.x, vel1.y, sin, cos, false);
                ball0.vx = vel0F.x;
                ball0.vy = vel0F.y;
                ball1.vx = vel1F.x;
                ball1.vy = vel1F.y;
            }
        }

        var checkWalls = function (ball) {
            if (ball.x + ball.radius > canvas.width) {
                //  ball.x = canvas.width - ball.radius;
                setBallX(ball, canvas.width - ball.radius)
                ball.vx *= bounce;
            } else
            if (ball.x - ball.radius < 0) {
                // ball.x = ball.radius;
                setBallX(ball, ball.radius)
                ball.vx *= bounce;
            }
            if (ball.y + ball.radius > canvas.height) {
                //  ball.y = canvas.height - ball.radius;
                setBallY(ball, canvas.height - ball.radius)
                ball.vy *= bounce;
            } else
            if (ball.y - ball.radius < 0) {
                //ball.y = ball.radius;
                setBallY(ball, ball.radius)
                ball.vy *= bounce;
            }
        }

        var move = function (ball) {
            ball.vy += _gravityY;
            ball.vx += _gravityX;
            setBallX(ball, ball.x + ball.vx)
            setBallY(ball, ball.y + ball.vy)
            checkWalls(ball);
        }
        var setBallX = function (ball, x) {
            if (isNaN(ball.pointerID)) {
                ball.x = x
            }
        }
        var setBallY = function (ball, y) {
            if (isNaN(ball.pointerID)) {
                ball.y = y
            }
        }

        var rand = function (min, max) {
            return Math.random() * (max - min) + min;
            return (Math.random() * max) + min;
        }

        self.initialize();
        return self;
    }

    window.log = function f() {
        log.history = log.history || [];
        log.history.push(arguments);
        if (this.console) {
            var args = arguments,
                newarr;
            args.callee = args.callee.caller;
            newarr = [].slice.call(args);

            if (typeof console.log === 'object') log.apply.call(console.log, console, newarr);
            else console.log.apply(console, newarr);
        }
    };
    (function (a) {
        function b() {}

        for (var c = "assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","), d; !!(d = c.pop());) {
            a[d] = a[d] || b;
        }
    })

    (function () {
        try {
            console.log();
            return window.console;
        } catch (a) {
            return (window.console = {});
        }
    }());
</script>

I have been trapped at this point in the code for about a week now and could really do with some genius' help!

Aims:

  • Add balls equivalent to the length of the image array.

  • Each ball with have its respective image as a centred background.

  • The balls will not leave the bounds of the canvas.

Relevant code:

  • initBalls()

  • addBall()

Thanks, Jason.

https://codepen.io/prtjohanson/pen/vPKQBg

What needed to be changed:

for (let i = 0; i < imagesArray.length; i++) {
  console.log(i);

  const imageArray = imagesArray[i];

  setTimeout(function() {
    var arrayImage = new Image();

    arrayImage.onload = function() {
      addBall(arrayImage, stageX / 2, 0);
    };

    arrayImage.src = imageArray;
  }, i * 1000);
}

By the time the setTimeout callback fired, your for loop had already finished and with a var declaration, the for loop iteration has no scope of its own, with a let, each iteration has its own scope like a function does.

If it must run on browsers that don't have let or const keywords, let me know, I can provide a solution for them as well

This will work in IE11 and other browsers that don't support ES6

 for (var i = 0; i < imagesArray.length; i++) {
  (function(imageArray) {

    setTimeout(function() {
      var arrayImage = new Image();

      arrayImage.onload = function() {
        console.log('Add'+i);
        addBall(arrayImage, stageX / 2, 0);
      };

      arrayImage.src = imageArray;
    }, i * 1000);
  })(imagesArray[i]);
}

To get the images centered, without them going out of the bounds of canvas, use a 2D transform on the beginBitmapFill operation:

var transform = new createjs.Matrix2D();
transform.appendTransform(-shape.radius, -shape.radius, 1, 1, 0);
shape.graphics.beginBitmapFill(img, "repeat", transform).drawCircle(0, 0, shape.radius);

As for there being not as many balls as there are URLs in the array, it seems that sometimes the image source URL prompts "I am not a robot" captcha. If replaced with URL-s under your control, the issue should go away.

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