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.