Pong game with Canvas and JS: ball is redrawn with each movement? - javascript

I'm doing the typical Pong game with just HTML canvas and JavaScript, but I've been stuck on this issue for a while now. I have checked similar Stackoverflow questions, but none of the answers worked for me.
Basically I have a window.onload function that renders the game (draw and move) every second. The ball moves, but draws a replica of itself with each movement. I think it's got something to do with the fact that I'm rendering both things each second, but I can't figure it out.
var framesPerSecond = 60;
setInterval(function() {
drawGame();
move();
}, 1000/framesPerSecond);
Here's the Fiddle.
Thanks.

Canvas needs to be cleared before redrawing:
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
Otherwise, it would keep modifying existing canvas state
See in action - https://jsfiddle.net/ew2d1quL/2/

You have to redraw from beginning or you save another canvas/image somewhere and draw this first before you draw the rest of the game
var canvas;
var canvasContext;
var printScore = document.createElement("p");
var score = 0;
window.onload = function() {
canvas = document.getElementById("gameCanvas");
canvasContext = canvas.getContext('2d');
var framesPerSecond = 60;
setInterval(function() {
drawGame();
move();
}, 1000/framesPerSecond);
var leftPaddle = {
x: 0,
y: (canvas.height/2) - 50,
width: 10,
height: 100,
color: "yellow",
score: 0
}
var rightPaddle = {
x: canvas.width - 10,
y: (canvas.height/2) - 50,
width: 10,
height: 100,
color: "yellow",
score: 0
}
var ball = {
x: canvas.width/2,
y: canvas.height/2,
radius: 9,
color: "white",
speed: 5,
velocityX: 5,
velocityY: 5,
}
function drawGame() {
// redraw background
canvasContext.fillStyle = 'black';
canvasContext.fillRect(0, 0, canvasContext.canvas.width, canvasContext.canvas.height);
drawRect(leftPaddle.x, leftPaddle.y, leftPaddle.width, leftPaddle.height, leftPaddle.color);
drawRect(rightPaddle.x, rightPaddle.y, rightPaddle.width, rightPaddle.height, rightPaddle.color);
drawCircle(ball.x, ball.y, ball.radius, ball.color);
}
function drawRect(x, y, w, h, color) {
canvasContext.fillStyle = color;
canvasContext.fillRect(x, y, w, h);
}
function drawCircle(x, y, r, color) {
canvasContext.fillStyle = color;
canvasContext.beginPath();
canvasContext.arc(x, y, r, 0, Math.PI*2, false);
canvasContext.fill();
canvasContext.closePath();
}
function move() {
ball.x += ball.velocityX;
ball.y += ball.velocityY;
if ( (ball.y + ball.radius > canvas.height ) || (ball.y - ball.radius < 0) ) {
ball.velocityY =- ball.velocityY;
} else if ( (ball.x + ball.radius > canvas.width ) || (ball.x - ball.radius < 0)) {
ball.velocityX =- ball.velocityX;
}
drawGame();
}
}
canvas {
margin-top: 0px auto;
border: 2px solid black;
background-color: black;
border-radius: 10px;
display: block;
width: 50%;
}
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Pong</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<script src='main.js'></script>
</head>
<body>
<canvas id="gameCanvas" width="800" height="600"></canvas>
</body>
</html>

Related

how can i create dynamic shapes in html?

I need to create a dynamic triangle, such that a user is able to grab either a vertex or side and move it. The angle should get updated in real-time. Unfortunately, I have no clue how one can do that. I started out by simply drawing the triangle using the following code.
<!DOCTYPE html>
<html>
<head>
<title>Triangle Canvas Example</title>
</head>
<body>
<canvas id="myCanvas" width="700" height="700"></canvas>
<script>
var canvasElement = document.querySelector("#myCanvas");
var context = canvasElement.getContext("2d");
context.beginPath();
context.strokeStyle = '#0000ff';
context.arc(400, 100, 8, 0, 2 * Math.PI, true);
context.stroke();
context.font = "15px Comic Sans MS";
context.fillStyle = "#0000ff";
context.textAlign = "center";
context.fillText("81", 390, 140);
context.beginPath();
context.arc(200, 300, 8, 0, 2 * Math.PI, true);
context.strokeStyle ='red';
context.stroke();
context.font = "15px Comic Sans MS";
context.fillStyle = "red";
context.textAlign = "center";
context.fillText("49", 230, 300);
context.beginPath();
context.arc(520, 400, 8, 0, 2 * Math.PI, true);
context.strokeStyle ='#008000';
context.stroke();
context.font = "15px Comic Sans MS";
context.fillStyle = "#008000";
context.textAlign = "center";
context.fillText("50", 500, 385);
// the triangle
context.beginPath();
context.moveTo(400, 100);
context.lineTo(200, 300);
context.lineTo(520, 400);
context.closePath();
// the outline
context.lineWidth = 2;
context.strokeStyle = '#666666';
context.stroke();
</script>
</body>
</html>
to get the following image. please see image
the problem is that my code is purely hardcoded, the angle value, the circle, and the triangle. My approach is probably wrong because I cannot see how one can turn all that into one dynamic image. I tried googling to see if someone created something similar but I was unable to find anything useful. Some insight on how to approach this problem would be great (a Youtube video or article would be greatly appreciated).
I created dynamic trinagle but i don't know math. Hope I help you.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Trinagle</title>
<style>
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
canvas {
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas width="500" height="500"></canvas>
<script>
let mousePosition = [0, 0];
let mouseClicked = false;
let dragged = null;
const getMousePosition = (canvas, event) => {
const rect = canvas.getBoundingClientRect();
const x = Math.min(Math.max(event.clientX - rect.left, 0), canvas.width);
const y = Math.min(Math.max(event.clientY - rect.top, 0), canvas.width);
return [x, y];
}
document.addEventListener('DOMContentLoaded', () => {
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
document.addEventListener('mousemove', (event) => {
mousePosition = getMousePosition(canvas, event);
});
document.addEventListener('mousedown', () => mouseClicked = true);
document.addEventListener('mouseup', () => mouseClicked = false);
const points = [
[50, 200, '#f00'],
[150, 50, '#0f0'],
[200, 150, '#00f'],
];
const radius = 10;
const draw = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const [mX, mY] = mousePosition;
for (const [index, [x, y, color]] of points.entries()) {
const hovered = (mX - x) ** 2 + (mY - y) ** 2 < radius ** 2;
const style = hovered ? 'fill' : 'stroke';
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, true); // Outer circle
ctx[`${style}Style`] = color;
ctx[style]();
if(hovered && mouseClicked) {
dragged = index;
}
if(dragged === index) {
points[index] = [...mousePosition, color];
if(!mouseClicked) dragged = null;
}
}
ctx.beginPath();
ctx.moveTo(points[0][0], points[0][1]);
ctx.lineTo(points[1][0], points[1][1]);
ctx.lineTo(points[2][0], points[2][1]);
ctx.lineTo(points[0][0], points[0][1]);
ctx.strokeStyle = '#000';
ctx.stroke();
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
});
</script>
</body>
</html>

Why can't I move my circle inside the canvas?

I'm trying to move two circles separately, but I can't seem to make the two circles move despite searching over w3school or youtube, I know how to move one circle but when I try to move two circle by creating a constructor it fails.
I'm sure this is a dumb question but I just can't help but ask.
let canvas = document.querySelector("#canvas");
let ctx = canvas.getContext("2d");
let ball = new drawCircle(50, 50, 20);
let ballTwo = new drawCircle(100, 100, 20);
function drawCircle(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
this.draw = function() {
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI *2);
ctx.fillStyle = "lightskyblue";
ctx.fill();
}
}
function draw() {
ball.x += 1;
ball.draw();
ballTwo.x += 1;
ballTwo.draw();
if (x + r > canvas.width || x < r) {
dx = -dx;
}
if (y + r > canvas.width || y < r) {
dy = -dy;
}
requestAnimationFrame(draw)
}
draw();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CĂ­rculo</title>
<style>
body {
margin: 0;
display: flex;
justify-content: center;
}
#canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
</body>
<script src="/js/canvasTwo.js"></script>
</html>

HTML Canvas - Draw Shape after Interval

The script below draws 4 curved rectangles with images in them. It draws them all at once despite using a setInterval of 2500 milliseconds before calling the function that draws them. I would like the first rectangle (i.e. top-left) to be drawn immediately, and then the other rectangles to be drawn after that one (with one being drawn every 2.5 seconds). Why does this function not do this and how can it be done? Any help will be appreciated.
var c=document.getElementById('game'),
ctx=c.getContext('2d');
var images = ['https://i.stack.imgur.com/tXmPa.png', 'https://i.stack.imgur.com/KGhCL.png', 'https://i.stack.imgur.com/s5xu4.png', 'https://i.stack.imgur.com/g6BO0.jpg']
var curvedRect = function(id, selectionnum, x, y, w, h) {
this.id = id;
this.selectionnum = selectionnum;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
curvedRect.prototype.makeCurvedRect = function() {
var img=new Image();
img.src=images[this.selectionnum];
ctx.beginPath();
ctx.lineWidth='8';
ctx.strokeStyle='white';
ctx.moveTo(this.x+10, this.y);
ctx.lineTo(this.x+this.w-10, this.y);
ctx.quadraticCurveTo(this.x+this.w, this.y, this.x+this.w, this.y+10);
ctx.lineTo(this.x+this.w, this.y+this.h-10);
ctx.quadraticCurveTo(this.x+this.w, this.y+this.h, this.x+this.w-10, this.y+this.h);
ctx.lineTo(this.x+10, this.y+this.h);
ctx.quadraticCurveTo(this.x, this.y+this.h, this.x, this.y+this.h-10);
ctx.lineTo(this.x, this.y+10);
ctx.quadraticCurveTo(this.x, this.y, this.x+10, this.y);
ctx.stroke();
ctx.drawImage(img, this.x+2.5, this.y+2.5, this.w-5, this.h-5);
}
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++) {
try {
setInterval(this.shapes[i].makeCurvedRect(), 2500);
}
catch(err) {}
}
}
var paint = new Paint(c);
var img1 = new curvedRect(1, 0, 200, 55, 150, 150);
var img2 = new curvedRect(2, 1, 375, 55, 150, 150);
var img3 = new curvedRect(3, 2, 200, 230, 150, 150);
var img4 = new curvedRect(4, 3, 375, 230, 150, 150);
paint.addShape(img1);
paint.addShape(img2);
paint.addShape(img3);
paint.addShape(img4);
paint.render();
canvas {
z-index: -1;
margin: 1em auto;
border: 1px solid black;
display: block;
background: #FF9900;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
<link rel='stylesheet' type='text/css' href='game.css' media='screen'></style>
</head>
<body>
<canvas id="game" width = "750" height = "500"></canvas>
</body>
</html>
Replace
setInterval(this.shapes[i].makeCurvedRect(), 2500);
which executes makeCurvedRect() immediately and supplies setInterval() with the return value with a bound function
setTimeout(curvedRect.prototype.makeCurvedRect.bind(this.shapes[i]), 2500 * i);
or a fat arrow function
setTimeout(() => this.shapes[i].makeCurvedRect(), 2500 * i);
where the delay increases by 2500 for each rectangle.

Resizing canvas without maintaining proportions

In this program, I have a bit of code that changes the width of a canvas in response to a change of a slider. However, I noticed that the canvas resizes to maintain its aspect ratio.
Is there any way to prevent this from happening so that the canvas simply gets squished/expanded from the sides? Alternatively, could I make it so that the balls do not change size with the canvas and maintain their original sizes?
Code:
<!DOCTYPE html>
<html>
<head>
</head>
<style>
</style>
<body>
<canvas id="my_canvas" width="600px" height="300px"></canvas>
<script>
var canvas = document.getElementById("my_canvas");
var c = canvas.getContext("2d");
//create ball container
var container = {
x: 0,
y: 0,
width: 600,
height: 300
};
//make balls array
var balls = [{
x: 50,
y: 100,
r: 20,
vx: 10,
vy:10,
color: 125
}, {
x: 150,
y: 80,
r: 20,
vx: 10,
vy: 10,
color: 105
}, {
x: 90,
y: 150,
r: 20,
vx: 10,
vy: 10,
color: 20
}, {
x: 100,
y: 50,
r: 20,
vx: 10,
vy: 10,
color: 80
}];
function animate() {
//draw container
c.fillStyle = "#000000";
c.fillRect(container.x, container.y, container.width, container.height);
//loop balls array
for (var i = 0; i < balls.length; i++) {
//draw balls
c.fillStyle = 'hsl(' + balls[i].color++ + ', 100%, 50%)';
c.beginPath();
c.arc(balls[i].x, balls[i].y, balls[i].r, 0, Math.PI * 2, true);
c.fill();
//animate balls
if (balls[i].x - balls[i].r + balls[i].vx < container.x || balls[i].x + balls[i].r + balls[i].vx > container.x + container.width) {
balls[i].vx = -balls[i].vx;
}
if (balls[i].y + balls[i].r + balls[i].vy > container.y + container.height || balls[i].y - balls[i].r + balls[i].vy < container.y) {
balls[i].vy = -balls[i].vy;
}
balls[i].x += balls[i].vx;
balls[i].y += balls[i].vy;
}
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
function myFunction () {
document.getElementById("my_canvas").style.width = document.getElementById("VolumeInputId").value + "px";
}
</script>
<form name = "volume">
<label for="volume">Volume</label>
<input type="range" name="VolumeInputName" id="VolumeInputId" value=600 min="300" max="600" onchange="myFunction()">
<output name="VolumeOutputName" id="VolumeOutputId"></output>
</form>
</body>
</html>
Resizing the canvas with style should not be done unless you want to see a pixelated picture.
var canvas = document.getElementById("my_canvas");
var c = canvas.getContext("2d");
c.beginPath();
c.moveTo(0,0);
c.lineTo(30,30);
c.stroke();
document.getElementById("my_canvas").style.width = "280px";
canvas {
border: 1px solid black;
}
<canvas id="my_canvas" width="60px" height="30px"></canvas>
As you can see a 60 x 30 canvas when scaled via style to 280px shows a pixelated straight line.
But if you use canvas.width and canvas.height you can change each one independently and without interpolated pixels.
var canvas = document.getElementById("my_canvas");
var c = canvas.getContext("2d");
canvas.width = 280;
canvas.height = 200;
c.beginPath();
c.moveTo(0,0);
c.lineTo(30,30);
c.stroke();
canvas {
border: 1px solid black;
}
<canvas id="my_canvas" width="60px" height="30px"></canvas>
In this case the canvas width and height are changed independently and the stright line size is still the same.
height and width are properties of the canvas object (not the context).
Here is a detailed fiddle with the original, resized by style and resized by properties.

Why isn't my drawing showing on canvas?

I drew various shapes and text on my canvas, but nothing is showing in the browser. There are no errors in Chrome's console. I opened it in another browser, thinking it may have been a problem with cache, but still nothing except the "Draw Canvas" button shows. Here it is in jsfiddle: https://jsfiddle.net/0Lakv2do/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
#content-wrapper {
width: 600px;
margin: 0 auto;
text-align: center;
}
#canvasRun {
background-color: #c00;
border: 0;
color: #fff;
}
</style>
</head>
<body>
<div id="content-wrapper">
<button id="canvasRun">Draw Canvas</button><br><br>
<canvas id="myCanvas" width="600" height="450"></canvas>
</div>
<script type="text/javascript">
var contentWrapper = document.getElementById('contentWrapper');
var runButton = document.getElementById('canvasRun');
var canvas = document.getElementById('myCanvas');
myCanvas.style.visibility="hidden";
runButton.addEventListener('click', showCanvas, false);
function showCanvas() {
myCanvas.style.visibility = "visible";
if (myCanvas.getContext){
var logo = new Image();
logo.src = 'IIT_SAT_stack_186_white.png';
function renderMyCanvas() {
var ctx = canvas.getContext('2d');
var linearGrad = ctx.createLinearGradient(0,0,0,450);
linearGrad.addColorStop(0, 'white');
linearGrad.addColorStop(1, 'black');
ctx.fillStyle=linearGrad;
ctx.fillRect(0,20,600,450);
ctx.font = "32px sans-serif";
ctx.fillStyle = 'red';
ctx.fillText("ITMD 565 Canvas Lab", 135, 75);
ctx.beginPath();
ctx.moveTo(15, 90);
ctx.lineTo(580, 90);
ctx.lineWidth = 3;
ctx.strokeStyle = 'red';
ctx.closePath();
ctx.stroke();
ctx.font = "14px sans-serif";
ctx.fillStyle = 'white';
ctx.fillText("", 15, 410);
ctx.fillText("", 15, 430);
ctx.drawImage(logo, 300, 360, 250, 60);
ctx.fillStyle= 'white';
ctx.fillRect(250, 250, 310, 100);
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var startAngle = 1.1 * Math.PI;
var endAngle = 1.9 * Math.PI;
var counterClockwise = false;
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle, counterClockwise);
ctx.lineWidth = 15;
ctx.arc.strokeStyle = 'yellow';
ctx.stroke();
ctx.setLineDash([10, 10]);
ctx.beginPath();
ctx.moveTo(270,300);
ctx.quadraticCurveTo(330, 220, 395, 300, 395, 300);
ctx.strokeStyle = 'black';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(395, 300);
ctx.quadraticCurveTo(450, 375, 540, 300);
ctx.strokeStyle = 'black';
ctx.stroke();
}
}
}
</script>
</body>
</html>
You have just copy/pasted a function inside your other function and you don't ever call it. You also have a different variable canvas there while in the outer one you have myCanvas.
When copy/pasting make sure you actually know what you are copying and how it works.
See a Fiddle with the function definition removed and the variable name fixed.

Categories

Resources