cloning and clicking on a moving HTML5 Canvas element using Javascript - javascript

I need to figure out how to make a canvas element become clickable as well duplicate the element on the click, with the same properties. I have tried a few tricks with finding the ball via logic and pointing to the radius and coordinates of the ball and creating a clone constructor/prototype but to no avail has it worked! Thank you for the help!
Code below:
<style type="text/css">
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#balling {
border:1px solid rgb(0,0,0);
}
</style>
</head>
<body>
<canvas id="balling" width="500" height="400"></canvas>
<!-- Javascript on the bottom to make page run faster -->
<script type="text/javascript" src="js/lib/jquery.js"></script>
<script type="text/javascript">
var canvas = document.getElementById('balling');
var context = canvas.getContext('2d');
// The Properties of the Circle and Position within the Viewport
var CircleOptions = {
posBall: {
x: 160,
y: 180
},
radius: 40,
startAngle: 0,
endAngle: Math.PI * 2,
anticlockwise: false,
radians: 0,
xMove: Math.random(),
yMove: Math.random(),
speed:2,
angle:80,
velocityX:1,
velocityY:1
};
//Math to make the ball move
function moveBall() {
CircleOptions.radians = CircleOptions.angle * Math.PI/180;
CircleOptions.xMove = Math.cos(CircleOptions.radians) * CircleOptions.speed * CircleOptions.velocityX;
CircleOptions.yMove = Math.sin(CircleOptions.radians) * CircleOptions.speed * CircleOptions.velocityY;
}
//Function to draw the ball
function DrawOptions() {
//Reset Canvas
context.fillStyle = "white";
context.fillRect(0, 0, canvas.width, canvas.height);
//Drawing of the ball
context.fillStyle = "rgb(142, 68, 173)";
context.beginPath();
context.arc(CircleOptions.posBall.x, CircleOptions.posBall.y, CircleOptions.radius, CircleOptions.startAngle, CircleOptions.endAngle, CircleOptions.anticlockwise);
context.closePath();
context.fill();
}
//finding the coordinates of the circle
function CircleCoordinates(CircleOptions) {
var left = CircleOptions.posBall.x - CircleOptions.radius,
top = CircleOptions.posBall.y + CircleOptions.radius,
right = CircleOptions.posBall.x + CircleOptions.radius,
bottom = CircleOptions.posBall.y - CircleOptions.radius;
}
// Animate and call the function to move the ball
setInterval(Move, 20);
//Function call for the ball
moveBall();
//The function to make it move, reset canvas for movement and color/create shape of ball
function Move() {
//Function call for drawing and pinpointing the coordinates
DrawOptions();
CircleCoordinates(CircleOptions);
//Power to make it move
CircleOptions.posBall.x += CircleOptions.xMove;
CircleOptions.posBall.y += CircleOptions.yMove;
//checks for ball hitting the Wall
if(CircleOptions.posBall.x > canvas.width || CircleOptions.posBall.x < 0) {
CircleOptions.angle -= 770;
moveBall();
} else if(CircleOptions.posBall.y > canvas.height || CircleOptions.posBall.y < 0) {
CircleOptions.angle -= 2760;
moveBall();
} else if(CircleOptions.posBall.y == canvas.height || CircleOptions.posBall.y > canvas.width) {
CircleOptions.angle += 90;
moveBall();
}
}
$('#balling').on('click', function(e) {
var clickedX = e.pageX - this.offsetLeft;
var clickedY = e.pageY - this.offsetTop;
if (clickedX > CircleOptions.right && clickedX > CircleOptions.left && clickedY > CircleOptions.top && clickedY < CircleOptions.bottom) {
alert ('clicked number ');
}
});
//Clone prototype and constructor
function Clone() {
Clone.prototype = CircleOptions;
var c = new Clone();
if(CircleOptions.posBall.x < canvas.width || CircleOptions.posBall.y == canvas.height) {
return c;
}
}
//function call for clone
Clone();
</script>
</body>
A link to the fiddle to see the code live: http://jsfiddle.net/coder101/CMW24/

For cloning DOM nodes use cloneNode. For debugging the code use jsfiddle, so the community could help you in spotting the problem with clicks
You had few bugs in your code. The corrected version:
function CircleCoordinates() {
CircleOptions.left = CircleOptions.posBall.x - CircleOptions.radius,
CircleOptions.right = CircleOptions.posBall.x + CircleOptions.radius,
CircleOptions.top = CircleOptions.posBall.y - CircleOptions.radius,
CircleOptions.bottom = CircleOptions.posBall.y + CircleOptions.radius;
}
...
canvas.addEventListener('click', function (e) {
var clickedX = e.pageX - this.offsetLeft;
var clickedY = e.pageY - this.offsetTop;
if (clickedX < CircleOptions.right && clickedX > CircleOptions.left && clickedY > CircleOptions.top && clickedY < CircleOptions.bottom) {
alert('clicked number ');
}
});
(Why do you need jQuery?)

Related

Issue when dragging text on canvas if slightly scrolled down

When I scroll slightly down on my canvas, it does not allow me to drag my text at all. Examples (These are GIFs) -
https://gyazo.com/e60d2efd924ced758c2c6441391804db
GIF explained: So you saw the drag was working when I was on top of the page but when I scrolled slightly down. It completely stopped working. I added a few console.logs around, and what I know is the click event listener for the canvas is working but it isn't detected the text when I slightly scroll down on the page.
I based my drag code from: http://jsfiddle.net/m1erickson/9xAGa/ | What you can see is if you change the canvas size width: 667 and height: 800, and when you scroll slightly down, you will have the same issue I am having.
HTML Code:
<div id="middle_container">
<div class="center_container">
<canvas id="canvas" width="667px" height="800px"></canvas>
</div>
</div>
JavaScript Code:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB=canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(const { text, x, y, width, height } of texts) {
ctx.fillText(text, x, y);
}
}
function addNewText(string_text) {
var y = texts.length * 20 + 20;
var text = {
text: string_text,
x: 20,
y: y
};
ctx.font = "32px verdana";
ctx.textBaseline = "top";
text.width = ctx.measureText(text.text).width;
text.height = 32;
texts.push(text);
draw();
}
function hitDrag(x,y,textIndex) {
var r=texts[textIndex];
return (x>r.x && x<r.x+r.width && y>r.y && y<r.y+r.height);
}
function myDrag(a,e) {
if (a == "down") {
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<texts.length;i++){
if(hitDrag(mx,my,i)){
console.log("found");
dragF = i;
}
}
}
}
addNewText("Hello World")
$("#canvas").mousedown(function(e) {
myDrag("down", e);
});
The problem is this line of code:
var BB=canvas.getBoundingClientRect();
which is only populated once at the start of your script. The .getBoundingClientRect() method returns the position of a HTML element relative to the viewport.
Well if you scroll the window, the viewport moves - as the canvas element - but the BB object still holds the position of your canvas at startup.
The fix is rather simple - you need to use the actual position of the canvas element by calling .getBoundingClientRect() again on mouse down and on mouse move.
I've prepared a little sample based on your code and the fiddle you've linked:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB = canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const {
text,
x,
y,
width,
height
} of texts) {
ctx.fillText(text, x, y);
}
}
function addNewText(string_text) {
var y = texts.length * 20 + 20;
var text = {
text: string_text,
x: 20,
y: y
};
ctx.font = "32px verdana";
ctx.textBaseline = "top";
text.width = ctx.measureText(text.text).width;
text.height = 32;
texts.push(text);
draw();
}
function myDrag(a, e) {
if (a == "down") {
e.preventDefault();
e.stopPropagation();
let rect = canvas.getBoundingClientRect();
mx = parseInt(e.clientX - rect.left);
my = parseInt(e.clientY - rect.top);
for (var i = 0; i < texts.length; i++) {
if (hitDrag(mx, my, i)) {
// console.log("found");
dragF = i;
}
}
}
}
function hitDrag(x, y, textIndex) {
var r = texts[textIndex];
return (x > r.x && x < r.x + r.width && y > r.y && y < r.y + r.height);
}
function handleMouseMove(e) {
if (dragF < 0) {
return;
}
e.preventDefault();
let rect = canvas.getBoundingClientRect();
mouseX = parseInt(e.clientX - rect.left);
mouseY = parseInt(e.clientY - rect.top);
var dx = mouseX - mx;
var dy = mouseY - my;
mx = mouseX;
my = mouseY;
var text = texts[dragF];
text.x += dx;
text.y += dy;
draw();
}
function handleMouseUp(e) {
e.preventDefault();
dragF = -1;
}
addNewText("Hello World")
$("#canvas").mousedown(function(e) {
myDrag("down", e);
});
$("#canvas").mousemove(function(e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function(e) {
handleMouseUp(e);
});
body {
background-color: ivory;
}
#canvas {
border: 1px solid red;
}
#theText {
width: 10em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="middle_container">
<div class="center_container">
<canvas id="canvas" width="667px" height="800px"></canvas>
</div>
</div>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>

How can I stop an event listener from slowing down my canvas animation?

I am working on a canvas animation and I just started adding event listeners. Unfortunately, when I added a listener to track the location of the cursor, the animation slows down significantly every time I move the mouse. If I click, it stops completely. I'm guessing it's too much to process, so is there a way to improve the run time of the animation? Would this work with Web Workers?
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var c = canvas.getContext('2d');
//Variables for the blue ball
var bx = Math.random() * innerWidth;
var by = Math.random() * innerHeight;
var bbdx = 1;
var bbdy = 1;
var bRadius = 12;
//Variables for the red balls
var rx = Math.random() * innerWidth;
var ry = Math.random() * innerHeight;
var rrdx = 1;
var rrdy = 1;
var rRadius = 12;
//Mouse coordinate object
var mouse = {
x: undefined,
y: undefined
}
function bCircle() {
c.beginPath();
c.arc(bx, by, bRadius, 0, Math.PI * 2, false);
c.strokeStyle = "white";
c.stroke();
c.fillStyle = "cornflowerblue";
c.fill();
c.closePath();
//Ball bouncing Conditional
}
function rCircle() {
c.beginPath();
c.arc(rx, ry, rRadius, 0, Math.PI * 2, false);
c.strokeStyle = "pink";
c.stroke();
c.fillStyle = "red";
c.fill();
c.closePath();
//Ball Bouncing Conditional
}
//Interactivity function
function bClick() {
window.addEventListener('mousemove', function(event) {
mouse.x = event.x;
mouse.y = event.y;
console.log(mouse);
});
}
function draw() {
c.clearRect(0, 0, innerWidth, innerHeight);
bCircle();
rCircle();
//bCircle Conditional
if (bx + bRadius > innerWidth || bx - bRadius < 0) {
bbdx = -bbdx;
}
//Conditional to mall the ball bounce up and down
if (by + bRadius > innerHeight || by - bRadius < 0) {
bbdy = -bbdy;
}
//Add 1 to x continously for it to move
bx += bbdx;
//Add 1 constantly to y for it to move up and down also
by += bbdy;
//rCircle Conditional
if (rx + rRadius > innerWidth || rx - rRadius < 0) {
rrdx = -rrdx;
}
if (ry + rRadius > innerHeight || ry - rRadius < 0) {
rrdy = -rrdy;
}
rx += rrdx;
ry += rrdy;
bClick();
}
setInterval(function() {
draw();
}, 8);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tap-Tap</title>
<style type="text/css">
canvas {
border: 1px solid black;
}
body {
margin: 0;
background-color: black;
}
</style>
</head>
<body>
<canvas></canvas>
<script src="ball.js" type="text/javascript"></script>
</body>
</html>
You're adding that "mousemove" event handler reduntantly on each call to draw(). The .addEventListener() API does not remove prior handlers already added. After a little while there'll be hundreds of them and the browser will call each and every one.
Call bClick() once outside the timer handler. Also that console.log() call in the "mousemove" handler doesn't help performance.

HTML Canvas & Javascript - Emulating a Scroll-On-Hover Event

I am trying to write a scroll-on-hover function in an HTML canvas by defining a hover variable, detecting mouse events over the designated hover area and (on doing so) adding or subtracting to this hover variable depending on which area is hovered over. This hover variable is connected to the position of a series of selection buttons which, for the purposes of this example, contain the numbers 0 to 30. When either end of this series of selection buttons is hovered over they all move up or down as if scrolled, but to make it act like a scroll you must keep the mouse moving as the canvas is only rendered on each new mousemove event.
My question is how can I trigger the event on mouseover such that if (lowerHoverBoxHitTest(x, y)) or (upperHoverBoxHitTest(x, y)) (i.e if the mouse is hovered over either of the hit boxes defined in the script below) the hover variable keeps being added to by the set increment (0.1) until the mouse leaves that area. I have tried replacing the if/else statement in the function mouseMove with a while loop (as it would seem this is logically akin to what I am asking) as so
while (lowerHoverBoxHitTest(x, y)) {
if (hover < 750) {
hover-=0.1;
}
}
while (upperHoverBoxHitTest(x, y)) {
if (hover > 0) {
hover+=0.1;
}
}
but this just causes the page to crash (presumably it triggers an infinite loop?). There isn't much on Stack Overflow about this besides this but this solution is not useful if you have a lot of other things in your canvas that you don't want to scroll (unless you were to define their position absolutely which I don't want to) which I do in my full project. Any help will be appreciated.
var c=document.getElementById('game'),
canvasX=c.offsetLeft,
canvasY=c.offsetTop,
ctx=c.getContext('2d');
var hover=0;
function upperHoverBoxHitTest(x, y) {
return (x >= 0) && (x <= 350) && (y >= 0) && (y <= 50);
}
function lowerHoverBoxHitTest(x, y) {
return (x >= 0) && (x <= 350) && (y >= 450) && (y <= 500);
}
var selectionForMenu = function(id, text, y) {
this.id = id;
this.text = text;
this.y = y;
}
selectionForMenu.prototype.makeSelection = function() {
ctx.beginPath();
ctx.fillStyle='#A84FA5';
ctx.fillRect(0, this.y+hover, 350, 30)
ctx.stroke();
ctx.font='10px Noto Sans';
ctx.fillStyle='white';
ctx.textAlign='left';
ctx.fillText(this.text, 10, this.y+hover+19);
}
var Paint = function(element) {
this.element = element;
this.shapes = [];
}
Paint.prototype.addShape = function(shape) {
this.shapes.push(shape);
}
Paint.prototype.render = function() {
ctx.clearRect(0, 0, this.element.width, this.element.height);
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].makeSelection();
}
}
var paint = new Paint(c);
for (i=0; i<30; i++) {
paint.addShape(new selectionForMenu(i+1, i, i*30));
}
paint.render();
function mouseMove(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
paint.render();
if (lowerHoverBoxHitTest(x, y)) {
hover+=1;
} else if (upperHoverBoxHitTest(x, y)) {
hover-=1;
}
}
c.addEventListener('mousemove', mouseMove);
canvas {
z-index: -1;
margin: 1em auto;
border: 1px solid black;
display: block;
background: #9F3A9B;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
<link rel='stylesheet' type='text/css' href='wordpractice.css' media='screen'>
</head>
<body>
<canvas id="game" width = "350" height = "500"></canvas>
</body>
</html>
Animation via animation loops.
You need to have an animation loop that will increment/decrement the value if the conditions are met. This loop can be part of another if you have one (which is better than adding an animation loop for each animated object) or as its own function.
The animation loop does all the rendering, and only if needed (no point rendering something that is already rendered).
Demo
Demo is a copy of the OP's code with modifications to animate the scrolling and give a little user feed back. Though not complete as a scrolling selection box, it will need some tweaking to be useful.
var c = document.getElementById('game'),
canvasX = c.offsetLeft,
canvasY = c.offsetTop,
ctx = c.getContext('2d');
var hover = 0;
const overTypes = {
lower : 1,
raise : 2,
none : 0,
}
var overBox = 0;
var overDist = 0;
const maxSpeed = 4;
const shapeSize = 30;
const hoverScrollSize = 50;
const gradUp = ctx.createLinearGradient(0, 0, 0, hoverScrollSize);
const gradDown = ctx.createLinearGradient(0, ctx.canvas.height - hoverScrollSize, 0, ctx.canvas.height);
gradUp.addColorStop(0, `rgba(${0xA8},${0x4F},${0xB5},1)`);
gradUp.addColorStop(1, `rgba(${0xA8},${0x4F},${0xB5},0)`);
gradDown.addColorStop(1, `rgba(${0xB8},${0x5F},${0xB5},1)`);
gradDown.addColorStop(0, `rgba(${0xB8},${0x5F},${0xB5},0)`);
c.addEventListener('mousemove', mouseMove)
c.addEventListener('mouseout', () => {
overBox = overTypes.none
}); // stop scroll when mouse out of canvas
// start the first frame
requestAnimationFrame(() => {
paint.render(); // paint first frame
requestAnimationFrame(mainLoop); // start main loop
});
function mainLoop() {
if (overBox !== overTypes.none) {
hover += overDist / hoverScrollSize * (overBox === overTypes.lower ? maxSpeed : -maxSpeed);
var bottom = - (paint.shapes.length - ctx.canvas.height / shapeSize) * shapeSize;
hover = hover > 0 ? 0 : hover < bottom ? bottom : hover;
paint.render();
}
requestAnimationFrame(mainLoop); // wait for next animation frame
}
function mouseMove(event) {
var x = event.clientX - canvasX;
var y = event.clientY - canvasY;
if (lowerHoverBoxHitTest(x, y)) {
overBox = overTypes.lower;
} else if (upperHoverBoxHitTest(x, y)) {
overBox = overTypes.raise;
} else {
overBox = overTypes.none;
}
}
function upperHoverBoxHitTest(x, y) {
overDist = hoverScrollSize - y;
return (x >= 0) && (x <= 350) && (y >= 0) && (y <= hoverScrollSize);
}
function lowerHoverBoxHitTest(x, y) {
overDist = y - (ctx.canvas.height - hoverScrollSize);
return (x >= 0) && (x <= 350) && (y >= ctx.canvas.height - hoverScrollSize) && (y <= ctx.canvas.height);
}
var selectionForMenu = function (id, text, y) {
this.id = id;
this.text = text;
this.y = y;
}
selectionForMenu.prototype.makeSelection = function () {
ctx.beginPath();
ctx.fillStyle = '#A84FA5';
ctx.fillRect(0, this.y + hover, 350, shapeSize)
ctx.stroke();
ctx.font = '10px Noto Sans';
ctx.fillStyle = 'white';
ctx.textAlign = 'left';
ctx.fillText(this.text, 10, this.y + hover + 19);
}
var Paint = function (element) {
this.element = element;
this.shapes = [];
}
Paint.prototype.addShape = function (shape) {
this.shapes.push(shape);
}
Paint.prototype.render = function () {
ctx.clearRect(0, 0, this.element.width, this.element.height);
for (var i = 0; i < this.shapes.length; i++) {
this.shapes[i].makeSelection();
}
if (overBox !== overTypes.none) {
ctx.globalAlpha = 0.4 * (overDist / 50);
ctx.globalCompositeOperation = "lighter";
if (overBox === overTypes.raise) {
ctx.fillStyle = gradUp;
ctx.fillRect(0, 0, ctx.canvas.width, hoverScrollSize);
} else if (overBox === overTypes.lower) {
ctx.fillStyle = gradDown;
ctx.fillRect(0, ctx.canvas.height - hoverScrollSize, ctx.canvas.width, hoverScrollSize);
}
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 1;
}
}
var paint = new Paint(c);
for (i = 0; i < 30; i++) {
paint.addShape(new selectionForMenu(i + 1, i, i * 30));
}
paint.render();
canvas {
z-index: -1;
margin: 1em auto;
border: 1px solid black;
display: block;
background: #9F3A9B;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
<link rel='stylesheet' type='text/css' href='wordpractice.css' media='screen'>
</head>
<body>
<canvas id="game" width = "350" height = "150"></canvas>
</body>
</html>

detecting collisions on the canvas

im trying to make a game where collisions happen between the protagonist and the antagonist. I cant get the collision to work though, i've tried using the x and y position then the x and y positions plus the width and the height of the protagonist and the antagonist
var canvas = document.getElementById('canvas');
var PX = 10;
var PY = 10;
var PW = 10;
var PH = 10;
var P = PX + PY;
var EX1 = 100;
var EY1 = 100;
var EW1 = 10;
var EH1 = 10;
var E1 = EX1 + EY1;
window.addEventListener("keydown", charMove);
window.addEventListener("keyup", charMove);
window.addEventListener("keypress", charMove);
window.onload = function() {
context = canvas.getContext("2d");
canvas.style.background = "black";
var framesPerSecond = 30;
setInterval(function() {
draw();
move();
}, 1000/framesPerSecond);
}
function draw() {
//EX context.fillRect(PosX, PosY, width, height);
//draws protagonist
context.beginPath();
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "blue"
context.fillRect(PX, PY, PW, PH);
context.stroke();
context.closePath();
//draws antagonist(s)
context.beginPath();
context.fillStlyle = "red";
context.fillRect(EX1, EY1, EW1, EH1);
context.stroke();
context.closePath();
}
function move() {
}
function charMove(){
var x = event.which || event.keyCode;
if(x == 37){
PX -= 1;
}
if(x == 38){
PY -= 1;
}
if(x == 39){
PX += 1;
}
if(x == 40){
PY += 1;
}
}
//detect collision
setInterval(function() {
if(PX > EX1 || PX + PW < EX1 + EW1 && PY + PH > EY1 + EH1 || PY + PH < EY1 + EH1){
window.alert("collision");
}
}, 1);
<html>
<head>
</head>
<body>
<canvas width="500px" height="500px" id="canvas" class="canvas">
</body>
</html>
Your formula for collision is wrong.
This problem is called Axis Aligned Bounding Box collision.
Two AABBs collide if their projections to each axis collide. In your 2-dimensinal case you have to consider the horizontal and vertical projections.
The projections are segments of 1-d space. Collision for those is very easy: if the start or the end of a segment is on the other they collide. Formally start2 <= start1 <= end2 or start2 <= end1 <= end2
In code:
intersects([p.x, p.x + p.width], [e.x, e.x + e.width]) && intersects([p.y, p.y + p.height], [e.y, e.y + e.height])
where
function intersects(seg1, seg2) {
return contains(seg1, seg2[0]) || contains(seg1, seg2[1])
}
function contains(segment, point) {
return segment[0] <= point && point <= segment[1]
}

Canvas collision

I am a new in javascript and trying to find out how to make a collision with ball and plank which will stop the game and alert player with something like "You lost". But I only want red balls to hit the plank and blue to pass on without touching. Here is code that I am working on. (I dont mind if you could help to do collision only with both balls)
var spawnRate = 100;
var spawnRateOfDescent = 2;
var lastSpawn = -10;
var objects = [];
var startTime = Date.now();
function spawnRandomObject() {
var t;
if (Math.random() < 0.50) {
t = "red";
} else {
t = "blue";
}
var object = {
type: t,
x: Math.random() * (canvas.width - 30) + 15,
y: 0
}
objects.push(object);
}
function animate() {
var time = Date.now();
if (time > (lastSpawn + spawnRate)) {
lastSpawn = time;
spawnRandomObject();
}
for (var i = 0; i < objects.length; i++) {
var object = objects[i];
object.y += spawnRateOfDescent;
ctx.beginPath();
ctx.arc(object.x, object.y, 8, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = object.type;
ctx.fill();
}
}
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var paddleHeight = 10;
var paddleWidth = 60;
var paddleY = 480
var paddleX = (canvas.width-paddleWidth)/2;
var rightPressed = false;
var leftPressed = false;
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
function keyDownHandler(e) {
if(e.keyCode == 39) {
rightPressed = true;
}
else if(e.keyCode == 37) {
leftPressed = true;
}
}
function keyUpHandler(e) {
if(e.keyCode == 39) {
rightPressed = false;
}
else if(e.keyCode == 37) {
leftPressed = false;
}
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, paddleY, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPaddle();
animate();
if(rightPressed && paddleX < canvas.width-paddleWidth) {
paddleX += 3;
}
else if(leftPressed && paddleX > 0) {
paddleX -= 3;
}
}
setInterval(draw, 10);
Thanks!
If you have an object like this:
let ball = { type: 'red', x: 10, y: 10, width: 10, height: 10 };
You might want to consider adding a method to this to check if it overlaps any other rectangle:
ball.overlapsBall = function( otherBall ){
return !(
otherBall.x + otherBall.width < this.x
&& otherBall.y + otherBall.height < this.y
&& otherBall.y > this.y + this.height
&& otherBall.x > this.x + this.height
);
}
You do this by checking if it does not overlap, which is only true if one box is entirely outside of the other (have a read through the if statement and try to visualise it, its actually rather simple)
In your draw function you could now add a loop to see if any overlap occurs:
var overlap = objects.filter(function( ball ) { return paddle.overlapsBall( ball ) });
You could even place an if statement to check it's type! (The filter will take you entire array of balls and check the overlaps, and remove anything from the array that does not return true. Now you can use overlaps.forEach(function( ball ){ /* ... */}); to do something with all the balls that overlapped your paddle.)
One last thing, if you are planning on doing this with many objects you might want to consider using a simple class like this for every paddle or ball you make:
class Object2D {
constructor(x = 0, y = 0;, width = 1, height = 1){
this.x = x;
this.y = x;
this.width = width;
this.height = height;
}
overlaps( otherObject ){
!( otherObject.x + otherObject.width < this.x && otherObject.y + otherObject.height < this.y && otherObject.y > this.y + this.height && otherObject.x > this.x + this.height );
}
}
This allows you to this simple expression to create a new object that automatically has a method to check for overlaps with similar objects:
var paddle = new Object2D(0,0,20,10);
var ball = new Object2D(5,5,10,10);
paddle.overlaps( ball ); // true!
On top of that, you are ensured that any Object2D contains the values you will need for your calculations. You can check if this object is if the right type using paddle instanceof Object2D (which is true).
Note Please note, as #Janje so continuously points out in the comments below, that we are doing a rectangle overlap here and it might create some 'false positives' for all the pieces of rectangle that aren't the circle. This is good enough for most cases, but you can find the math for other overlaps and collisions easily ith a quick google search.
Update: Simple Implementation
See below for a very simple example of how overlaps work in action:
var paddle = { x: 50, y: 50, width: 60, height: 20 };
var box = { x: 5, y: 20, width: 20, height: 20 };
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
document.body.appendChild( canvas );
canvas.width = 300;
canvas.height = 300;
function overlaps( a, b ){
return !!( a.x + a.width > b.x && a.x < b.x + b.width
&& a.y + a.height > b.y && a.y < b.y + b.height );
}
function animate(){
ctx.clearRect( 0, 0, canvas.width, canvas.height );
ctx.fillStyle = overlaps( paddle, box ) ? "red" : "black";
ctx.fillRect( paddle.x, paddle.y, paddle.width, paddle.height );
ctx.fillRect( box.x, box.y, box.width, box.height );
window.requestAnimationFrame( animate );
}
canvas.addEventListener('mousemove', function(event){
paddle.x = event.clientX - paddle.width / 2;
paddle.y = event.clientY - paddle.height / 2;
})
animate();

Categories

Resources