Accessing array from objects method within array - javascript

I have researched this question but cannot find an answer. I am new to JavaScript arrays and method scope.
I have a circles array which contains Circle Object. This has an update method which ultimately lets it draw itself to the canvas and move
Problem is the Circles need to be able to access the location of other Circles in their circles array to see if they've collided.
But when I use the Circles method they don't recognise their holding array - circles:
let circles;
function init() {
circles = [];
for (i = 0; i < 5; i++) {
let dy = (Math.random() - 0.5) * 8;
let radius = Math.random() * 30;
let color = colors[Math.floor(Math.random() * colors.length)];
let x = Math.random() * (innerWidth - radius * 2) + radius;
let dx = (Math.random() - 0.5) * 8;
let y = Math.random() * (innerHeight - radius * 2) + radius;
circles.push(new Circle(x, y, dx, dy, radius, color));
}
}
function animate() {
requestAnimationFrame(animate);
c.clearRect(0, 0, innerWidth, innerHeight);
circles.forEach(circle => {
circle.update();
});
}
function Circle(x, y, dy, dx, radius, color) {
this.x = x;
this.y = y;
this.dy = dy;
this.dx = dx;
this.radius = radius;
this.color = color;
this.update = function (circles) {
for (i = 0; i < circles.length; i++) {
if (this === circles[i]) {
continue;
}
if (getDistance(this.x, this.y, circles[i].x, circles[i].y) - radius * 2 < 0) {
console.log('TOUCH')
}
}
this.draw();
}
this.draw = function () {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
// c.strokeStyle = 'black';
c.fillStyle = this.color;
c.fill();
c.closePath();
}
}
This function leads to error:
Uncaught TypeError: Cannot read property 'length' of undefined
Because it doesn't recognise the circles array.
How can I give my objects method access to the array the objects in?

Your update method on Circle expects a circles array to be passed, but you're not passing anything:
function animate() {
requestAnimationFrame(animate);
c.clearRect(0, 0, innerWidth, innerHeight);
circles.forEach(circle => {
circle.update(circles); // <--- HERE
});
}

Related

Create a collision region on canvas elements That interacts with mouse Events

I want to create a collision region around a canvas element that enables me to interact with that element using mouse events width vanilla javascript.
To elaborate more on my problem here is the following:
at first I make an arc segment constructor with x, y, radius, beginAngle, endAngle, and a color arguments
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
/* arc class constructor */
function ArcSegment(x, y, radius, beginAngle, endAngle, segColor) {
this.x = x;
this.y = y;
this.radius = radius;
this.beginAngle = beginAngle;
this.endAngle = endAngle;
this.segColor = segColor;
this.update = function() {
this.draw();
}
this.draw = function(){
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, this.beginAngle, this.endAngle, false);
ctx.lineWidth = 20;
ctx.strokeStyle = this.segColor;
ctx.stroke();
}
}
Secondly, i add some value to create those arc segments
/* x, y, radius, startAngle, endAngle and color */
var centerX = canvas.width/2;
var centerY = canvas.height/2;
var radiuses = [
100,
120
];
var pi = Math.PI;
var segmentStart = [
pi/2,
0
];
var segmentRotation = [
1.4*pi,
0.2*pi
];
var segmentColors = [
"#133046",
"#15959F"
];
Then, i draw Them on the canvas.
var segment1 = new ArcSegment(centerX, centerY, radiuses[0], segmentStart[0], segmentStart[0]+segmentRotation[0], segmentColors[0]);
segment1.update();
var segment2 = new ArcSegment(centerX, centerY, radiuses[1], segmentStart[1], segmentStart[1]+segmentRotation[1], segmentColors[1]);
segment2.update();
and here is the result:
What i want now is a way to create a collision detection on top of each arc segment created, so when a mouse is clicked or moved on top of that specific arc segment
a sequence of events can occur (like a rotation animation for example or so...).
all the research i've done suggest to get the x and y value of a rectangle and calculate the distance of mouse position (mouse.x, mouse.y) and the length of the rectangle, but that method doesn't work with an arc segment with a lineWidth property.
Any help on the subject would be very appreciated.
Below is a pure mathematical approach, the key here is the code isPointInside
// Classes
function Arc(x, y, angle, arc, radius, colour, highlightColour) {
this.x = x;
this.y = y;
this.angle = angle;
this.arc = arc;
this.radius = radius;
this.colour = colour;
this.highlightColour = highlightColour;
this.highlighted = false;
this.lineWidth = 20;
}
Arc.prototype = {
isPointInside: function(x, y) {
var _x = x - this.x;
var _y = y - this.y;
var distance = Math.sqrt(_x * _x + _y * _y);
var invDistance = 1.0 / distance;
var angle = Math.acos(
_x * Math.cos(this.angle) * invDistance +
_y * Math.sin(this.angle) * invDistance
);
return distance > (this.radius - this.lineWidth/2) &&
distance < (this.radius + this.lineWidth/2) &&
angle < this.arc/2;
},
render: function(ctx) {
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.highlighted ? this.highlightColour : this.colour;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, this.angle - this.arc/2, this.angle + this.arc/2, false );
ctx.stroke();
}
};
// Variables
var canvas = null;
var ctx = null;
var arcs = [];
// Functions
function draw() {
ctx.fillStyle = "gray";
ctx.fillRect(0, 0, 999, 999);
for (var i = 0; i < arcs.length; ++i) {
arcs[i].render(ctx);
}
}
// Event Listeners
function onMouseMove(e) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
for (var i = 0; i < arcs.length; ++i) {
arcs[i].highlighted = arcs[i].isPointInside(x, y);
}
draw();
}
// Entry Point
onload = function() {
canvas = document.getElementById("canvas");
canvas.onmousemove = onMouseMove;
ctx = canvas.getContext("2d");
arcs.push(new Arc(190, 75, 0.2, 1.8, 60, "blue", "lime"));
arcs.push(new Arc(90, 75, 3.5, 4.2, 60, "red", "lime"));
draw();
}
<canvas id="canvas"></canvas>

Constructor, loops and arrays practice in vanilla JS

I'm trying to make an animation where particles flow from one side the other like a flock of birds. You can see a live version on my semi-finished portfolio here: https://benjamingibbsportfolio.000webhostapp.com/
I'm in the process of learning constructor functions, so I've decided to redo the above project using that type of programming.
I've managed to basically complete it, apart from the fact it only displays one particle/flake - it should show 100?
for(var i = 0; i < 100; i++)
{
flakes[i] = new Flake();
}
Where have I gone wrong?
Are all the particles simply being drawn at the same place?
I've uploaded the code here: https://jsfiddle.net/q7ja8qxv/
A single flake is drawn with this function:
this.display = function() {
ctx.clearRect(0, 0, w, h);
ctx.fillStyle = "#ffff00";
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, true);
ctx.fill();
}
The source of the problem is the first line of the function:
ctx.clearRect(0, 0, w, h);
This clears the whole canvas...
This means that every flake "erases" the previous flake(s).
As a fix you have to remove the ctx.clearRect() call from Flake.display() and instead call it in a place where it is only executed once, before you start drawing the flakes. For example in drawFlakes() right before the loop:
function drawFlakes() {
ctx.clearRect(0, 0, w, h);
for (i = 0; i < flakes.length; i++) {
flakes[i].display();
flakes[i].move();
flakes[i].update();
}
angle += 0.01;
}
Complete example:
var canvas = document.getElementById('stars');
var ctx = canvas.getContext('2d')
var w = window.innerWidth;
var h = window.innerHeight;
var flakes = [];
var angle = 0;
canvas.width = w;
canvas.height = h;
for (var i = 0; i < 20; i++) {
flakes[i] = new Flake();
}
function drawFlakes() {
ctx.clearRect(0, 0, w, h);
for (i = 0; i < flakes.length; i++) {
flakes[i].display();
flakes[i].move();
flakes[i].update();
}
angle += 0.01;
}
function Flake() {
this.x = Math.random() * w;
this.y = Math.random() * h;
this.r = Math.random() * 5 + 2;
this.d = Math.random() * 1;
this.display = function() {
ctx.fillStyle = "#ffff00";
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, true);
ctx.fill();
}
this.move = function() {
this.y += Math.pow(this.d, 2) + 1;
this.x += Math.sin(angle) * 60;
};
this.update = function() {
if (this.y > h) {
this.x = Math.random() * w;
this.y = 0;
this.r = this.r;
this.d = this.d;
}
};
}
setInterval(drawFlakes, 25);
body {
background: black;
overflow: hidden;
}
<canvas id="stars"></canvas>

Js object in a canvas game Uncaught TypeError: Cannot read property 'x' of undefined

I am making a canvas game, which draws an object on the canvas. But it keeps giving me an error x is undefined. It was working before but, when I added the star functions it broke. Any help as I am at a loss.
var spaceShip = {
position: {
x: canvas.width / 2,
y: canvas.height - 20
},
size: {
width: 50,
height: 12
},
Velocity: {
x: 20
},
drawSpaceShip: function(){ // Draw Spaceship Object
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.fillRect(this.position.x, this.position.y, this.size.width, this.size.height);
ctx.fillRect(this.position.x + 15, this.position.y, this.size.width - 30, this.size.height / 2 - 12);
ctx.fillRect(this.position.x + 22.5, this.position.y, this.size.width - 45, this.size.height / 2 - 15);
ctx.closePath();
ctx.fill();
requestAnimationFrame(this.drawSpaceShip);
}// End drawShip function
};// End spaceShip Object
function Star(x, y, rad, velocity, fill){
this.x = Math.floor(Math.random() * 599);//this create a random number between 0 and 599 on the x axis
this.y = 0;
this.rad = Math.floor((Math.random() * 30) + 1);//this create a random number between 10 and 30 for the radius
this.velocity = 6;
this.fill = fill
this.draw = function(){
ctx.beginPath();
ctx.fillStyle = this.fill;
ctx.arc(this.x, this.y, this.rad, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
this.y += this.velocity;
}
}
function createMultipleStars(){
for (var i = 0; i <= 4; i++)
stars[i] = new Star(i * 50, 10, i, i, "rgba(255,215,0,0.6)");
}
createMultipleStars();
function step() {
ctx.clearRect(0,0,canvas.width, canvas.height);
for (var i = 0; i<= 4; i++)
stars[i].draw();
requestAnimationFrame(step);
}
spaceShip.drawSpaceShip();
step();
You are loosing spaceShip object context when detaching this.drawSpaceShip method and passing it to requestAnimationFrame function. In this case drawSpaceShip is invoked with global object context (this === window). You can bind context explicitly with Function.prototype.bind method:
requestAnimationFrame(this.drawSpaceShip.bind(this));

Drawing Object Arrays with JS Canvas

I'm new to JS, and I have not gone into jQuery or such yet. All that I'm trying to do is draw 4 simple circles on the canvas. I put the objects into an array because I planned to possibly expand in the future, but that information is irrelevant.
What I need help with, is understanding the basics of how to create objects within an array and then display them on a canvas. Here is my dysfunctionate code:
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
var W = 400, H = 500
canvas.width = W, canvas.height = H;
var ball = [3];
for (i===0;i<=3; i++){
ball[i] = {
x: (Math.floor(Math.random() * 350) + 50),
y: (Math.floor(Math.random() * 450) + 50),
color: "red",
radius: (Math.floor(Math.random() * 35) + 15),
draw: balls(x, y, color, radius)
};
}
function balls(x, y, color, radius){
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2*Math.PI, false);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
You are making it very complex use this method:
function ball(){
this.x = Math.random() * 100;
this.y = Math.random() * 100;
this.radius = Math.random() * 10;
this.color = "#" + (Math.random() * 16777215).toString(16);
this.draw = function(){
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2*Math.PI, false);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
}
var balls = [];
for(var i = 0; i < 4; i++) {
balls[i] = new ball();
balls[i].draw();
}
You can simply assign draw to the balls function like so:
ball[i] = {
....
draw: balls
};
Then to draw it, once you defined ball[i] call it's draw() function:
var b = ball[i];
b.draw(b.x, b.y, b.color ,b.radius);
Example Here
Edit: Using no parentheses like ball, is how to reference the function ball() itself. And one last thing, you need to change for (i===0;i<=3; i++) to for (i=0;i<=3; i++). The === is a strict comparison operator, = is assignment.

Canvas bouncing balls showing an elliptical black ball

JSFiddle
Intended behavior
Sample values: Velocity: 10; Angle: 45; Color: red; Radius: 50.
On clicking "Shoot!" button, ball should move until it finally disappears behind one of the walls. Note that I want to simulate real world ball with gravity.
Each time we click Shoot, one more ball should be added to the balls array which will also be drawn.
What happens
A black ellipse is shown on clicking shoot once/multiple time. No console errors seen.
Code:
(function () {
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
WIDTH = canvas.width,
HEIGHT = canvas.height;
// our ball object
function Ball(radius, color) {
this.radius = radius;
this.color = color;
this.x = 50; // start coordinates
this.y = 50;
this.velX = 0;
this.velY = 0;
this.accX = 0;
this.accY = 0;
this.gravity = 0;
this.start = function (angle, velocity) {
this.gravity = 9.8;
this.angle = angle / 180 * Math.PI; // convert to radians
this.velX = velocity * Math.cos(this.angle);
this.velY = velocity * Math.sin(this.angle);
this.accX = 0; // zero intially
this.accY = 0; // TODO: set option for user to set himself
};
this.update = function () {
this.velY -= this.gravity;
};
this.draw = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.fillSyle = this.color;
ctx.fill();
ctx.closePath();
}
}
// balls array
var balls = [];
document.querySelector("input[type='button']").onclick = function () {
var color = gId("color").value, // getElementById; defined in jsfiddle
velocity = gId("velocity").value,
angle = gId("angle").value,
radius = gId("radius").value;
var ball = new Ball(radius, color);
ball.start(angle, velocity);
balls.push(ball);
};
setInterval(function () {
for (var i = 0, len = balls.length; i < len; i++) {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
balls[i].draw();
balls[i].update();
}
}, 1000 / 60); // 1000/x depicts x fps
I have no idea why it doesn't work. System: Windows 7 on Chrome/Firefox latest.
Any help is appreciated. Please comment for more information.
1) Set width and height attributes on your canvas element instead of applying a css style i.e:
<canvas width="400px" height="400px">You don't support canvas.</canvas>
2) divide the value of gravity by 60 because your update function is invoked every 1/60th of a second i.e:
this.start = function (angle, velocity) {
this.gravity = 9.8 / 60;
this.angle = angle / 180 * Math.PI; // convert to radians
this.velX = velocity * Math.cos(this.angle);
this.velY = velocity * Math.sin(this.angle);
this.accX = 0; // zero intially
this.accY = 0; // TODO: set option for user to set himself
};
3) change the update function to:
this.update = function () {
this.velY += this.gravity;
this.x += this.velX;
this.y += this.velY;
};
4) move the ctx.clearRect method outside the for loop otherwise you will only see one ball animating always i.e
setInterval(function () {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
for (var i = 0, len = balls.length; i < len; i++) {
balls[i].draw();
balls[i].update();
}
}, 1000 / 60); // 1000/x depicts x fps
Here the updated js-fiddle: http://jsfiddle.net/km6ozj6L/1/

Categories

Resources