So I have a simple canvas animation I want to start and stop using a toggle switch. The animation works fine and starts up, but the clearTimeout does not seem to work.
I made sure the variable "timeOut" is defined globally so it should have access to it, that seemed to be the usual advice in past questions similar to this one. Am I make a mistake somewhere else? Tried it on Firefox, Chrome and Chromium.
let timeOut;
function circles() {
let canvas = document.querySelector('canvas');
canvas.width = document.querySelector('canvas').offsetWidth;
canvas.height = document.querySelector('canvas').offsetHeight;
let c = canvas.getContext('2d');
let topRadius = 30;
let colorArray = [
'#ff0000',
'#00ff00',
'#0000ff',
]
let mouse = {
x: undefined,
y: undefined
}
window.addEventListener('mousemove', function(e) {
mouse.x = e.x;
mouse.y = e.y;
})
window.addEventListener('resize', function() {
canvas.width = document.querySelector('canvas').offsetWidth;
canvas.height = document.querySelector('canvas').offsetHeight;
init();
});
function Circle(x, y, dx, dy, radius) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dx;
this.radius = radius;
this.minRadius = radius;
this.color = colorArray[Math.floor(Math.random() * colorArray.length)];
this.draw = function() {
if (this.radius < 0) {
this.radius = this.minRadius;
}
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
c.fillStyle = this.color;
c.fill();
}
this.update = function() {
if (this.y + this.radius > innerHeight || this.y - this.radius <= 0) {
this.dy = -this.dy;
}
if (this.x + this.radius > canvas.width|| this.x - this.radius <= 0) {
this.dx = -this.dx;
}
this.x+=this.dx;
this.y+=this.dy;
if (mouse.x - this.x < 50 && mouse.x - this.x > -50 && mouse.y - this.y < 50 && mouse.y - this.y > -50) {
if (this.radius < topRadius) {
this.radius += 1;
}
} else if (this.radius > this.minRadius){
this.radius -= 1;
}
this.draw();
}
}
let circleArray = [];
function init() {
circleArray = [];
for (let i =0; i<50; i++) {
let radius = Math.random() * 10;
let x = Math.random() * (canvas.width - radius*2) + radius;
let y = Math.random() * (canvas.height - radius * 2) + radius;
let dx = (Math.random() - 0.5) * 4;
let dy = (Math.random() - 0.5 )* 4;
circleArray.push(new Circle(x, y, dx, dy, radius));
}
}
function animate() {
requestAnimationFrame(animate);
c.clearRect(0,0, canvas.width, innerHeight);
for(let i=0; i < circleArray.length; i++) {
circleArray[i].update();
}
}
init();
animate();
}
$('#startstop').click(function() {
if(!timeOut) {
timeOut = setTimeout(circles, 1000);
} else {
clearTimeout(timeOut);
timeOut = null;
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="display: flex; justify-content: space-around;">
<canvas style="width: 100px; height: 800px; background: red;"></canvas>
<div id="startstop">toggle</div>
</div>
You were super close to the solution ;)
in the function animate add line #3 (before for loop), that is having this value:
if (!timeOut) return;
Good luck!
This animation is not stopping because you call clearTimeout on your setTimeout but that only runs once. Your issue is further into the animate function. This relatively loops the requestAnimationFrame. You can store this as a request and then cancelAnimationFrame
Related
I am currently trying to build a Digital Table Tennis game and I have a lot in place so far, but whenever I run the program, the non-AI player's bat (The green rectangle) isn't showing up. It doesn't seem to be there at all. Is there some bug in my code? Also make sure to mainly pay attention to the bottom of the code, where the animate() function is declared. The outcome so far is here: https://Digital-Table-Tennis.programprodigy.repl.co. PLS HELP!
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
canvas.width = innerWidth;
canvas.height = innerHeight;
let mouse = {
x: null
}
window.onmousemove = e => {
mouse.x = e.x;
}
class Ball {
constructor() {
this.dx = Math.random() * 4 - 2;
this.dy = Math.random() * 4 - 2;
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
}
draw() {
ctx.fillStyle = 'orange';
ctx.beginPath();
ctx.arc(this.x, this.y, 10, 0, Math.PI * 2);
ctx.fill();
}
update() {
this.x += this.dx;
this.y += this.dy;
if (
this.x >= mouse.x &&
this.x <= mouse.x + 50 &&
this.y >= canvas.height - 5 &&
this.y <= canvas.height
) {
this.dx = -this.dx;
this.dy = -this.dy;
}
if (
this.x <= 0 ||
this.x >= canvas.width ||
this.y <= 0 ||
this.y >= canvas.height
) {
this.dx = -this.dx;
this.dy = -this.dy;
}
this.draw();
}
}
let ball = new Ball();
class AI {
constructor(lvl) {
this.x = 0;
this.color = 'red';
this.speed = lvl * 5;
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, 0, 50, 5);
}
update() {
if (ball.y < canvas.height / 2) {
let distance = ball.x - this.x;
this.x += distance / this.speed;
}
if (
ball.x >= this.x &&
ball.x <= this.x + 50 &&
ball.y >= 0 &&
ball.y <= 5
) {
ball.dx = -ball.dx;
ball.dy = -ball.dy;
}
this.draw();
}
}
let ai_player = new AI(5);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'red';
ctx.fillRect(mouse.x - 25, canvas.height - 5, 50, 5);
ball.update();
ai_player.update();
requestAnimationFrame(animate);
}
animate();
I actually have a red player rectangle on my end, however I had to scroll down to see it since the canvas height was more than my viewport.
You specified that the player rectangle should be green, but it's declared in animate() as ctx.fillStyle = 'red';.
As for solving the scrolling issues, it should be a quick CSS fix by specifying a max-width of 100vw and a max-height of 100vh on your canvas element, assuming you don't have any conflicting styles.
I have seen that this type of question has already been answered, however every answer is a little vague or has some cross-compatibility issues or is too beyond my understanding.So is there anyone who can give my an up-to-date, full,clear (showing and explaining all the steps involved) answer?I would like not to make the circles grow in size when the mouse is outside the window.I am not using JQuery(featured in many answers).Should I use it?Is there any way to implement this with pure javascript?
Thank you for your help and sorry if my code is a little bit redundant :)
var canvas = document.querySelector("canvas");
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var mouse = {
x: undefined,
y: undefined
}
window.addEventListener("mousemove", function(event){
mouse.x = event.x;
mouse.y = event.y;
})
window.addEventListener("resize", function(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
})
var colorArray = [
"#6CC61D",
"#E05132",
"#278E83",
"#A633D4"
]
function Circle(x,y,dx,dy,radius){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.color = colorArray[Math.floor(Math.random() * colorArray.length)];
this.draw = function(){
c.beginPath();
c.arc(this.x,this.y,this.radius,0,Math.PI*2,false);
c.fillStyle = this.color;
c.fill()
}
this.update = function(){
if (this.x + this.radius>window.innerWidth || this.x -this.radius<0){
this.dx = - this.dx;}
if (this.y + this.radius > window.innerHeight || this.y - this.radius < 0) {
this.dy = - this.dy;
}
if (mouse.x - this.x < 50 && mouse.x - this.x > - 50 && mouse.y - this.y < 50 && mouse.y - this.y > - 50 && this.radius<50 ){
this.radius += 1;
}
else if(this.radius>2){
this.radius -= 1;
}
this.x += this.dx;
this.y += this.dy;
this.draw();
}
}
var circleArray = [];
for (var i = 0; i < 600; i++) {
var x = Math.random() * (window.innerWidth - radius * 2) + radius;
var y = Math.random() * (window.innerHeight - radius * 2) + radius;
var dy = (Math.random() - 0.5) * 8;
var dx = (Math.random() - 0.5) * 8;
var radius = 30;
circleArray.push(new Circle(x, y, dx, dy, radius));
}
function animate(){
requestAnimationFrame(animate);
c.clearRect(0,0,window.innerWidth,window.innerHeight);
for (var i = 0;i < circleArray.length;i++){
circleArray[i].update()
}
circle.update();
}
animate();
I am not using JQuery (featured in many answers). Should I use it ?
Is there any way to implement this with pure javascript ?
It's according to your preferences. As JQuery is javascript, if you can do it with JQuery, you can do it with JS only. For what is about "Is it implementable ?", most likely. I will start to format a bit your code to decouple the logic (calculs/updating positions, speed & radius) from the rendering (drawing), it will be easier to operate.
Checking if the mouse is out of window :
You can use the mouseout event to detect when the mouse leave the window
window.addEventListener('mouseout', action);
Avoid the circles to grow in size when the mouse is outside the window :
For the action, set back the mouse.x & mouse.y to undefined
window.addEventListener('mouseout', function(){
mouse.x = undefined;
mouse.y = undefined;
})
and then add 2 more conditions in your Circle update method to only grow the radius if, in addition of the others conditions, mouse.x & mouse.y are defined (so if the mouse is in the window).
if (mouse.x &&
mouse.y &&
Math.abs(mouse.x - this.x) < 50 &&
Math.abs(mouse.y - this.y) < 50 &&
this.radius < 50 ) {
this.radius += 1;
}
Result :
const canvas = document.querySelector("canvas");
const c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const mouse = {
x: undefined,
y: undefined
}
window.addEventListener("mousemove", function(event){
mouse.x = event.x;
mouse.y = event.y;
})
window.addEventListener("resize", function(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
})
window.addEventListener('mouseout', function(){
mouse.x = undefined;
mouse.y = undefined;
})
const colorArray = [
"#6CC61D",
"#E05132",
"#278E83",
"#A633D4"
]
function Circle(x, y, dx, dy, radius){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.color = colorArray[Math.floor(Math.random() * colorArray.length)];
this.draw = function(){
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
c.fillStyle = this.color;
c.fill()
}
this.update = function(){
if (this.x + this.radius > window.innerWidth || this.x - this.radius < 0) {
this.dx = - this.dx;
}
if (this.y + this.radius > window.innerHeight || this.y - this.radius < 0) {
this.dy = - this.dy;
}
if ( mouse.x &&
mouse.y &&
Math.abs(mouse.x - this.x) < 50 &&
Math.abs(mouse.y - this.y) < 50 &&
this.radius < 50 ) {
this.radius += 1;
} else if(this.radius > 2) {
this.radius -= 1;
}
this.x += this.dx;
this.y += this.dy;
}
}
const circleArray = [];
for (let i = 0; i < 600; i++) {
const radius = 30;
const x = Math.random() * (window.innerWidth - radius * 2) + radius;
const y = Math.random() * (window.innerHeight - radius * 2) + radius;
const dy = (Math.random() - 0.5) * 8;
const dx = (Math.random() - 0.5) * 8;
circleArray.push(new Circle(x, y, dx, dy, radius));
}
function clearCanvas(){
c.clearRect(0,0,window.innerWidth,window.innerHeight);
}
function update(){
for (let i = 0; i < circleArray.length; i++){
circleArray[i].update()
}
}
function draw(){
for (let i = 0; i < circleArray.length; i++){
circleArray[i].draw()
}
}
function animate(){
requestAnimationFrame(animate);
clearCanvas();
update();
draw();
}
requestAnimationFrame(animate);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<canvas id="signature-pad" class="signature-pad" width=400 height=200></canvas>
</body>
</html>
I have practically no experience in JS (I mostly program in C#). I'm trying to create a random layout of circles (stars) in an html5 canvas and I have them moving at a random x and y velocity. I created a Star class to make many of these objects and manipulate them, but when I call the Update() method, I receive this TypeError:
TypeError: Cannot read property 'Update' of undefined
at animate (file:///F:/Code/Website/main.js:67:20)
at file:///F:/Code/Website/main.js:71:1
Here is the code that is throwing the error:
//Circle object
class Star
{
constructor(X, Y, VX, VY, R)
{
this.x = X;
this.y = Y;
this.vx = VX;
this.vy = VY;
this.r = R;
}
draw() {
c.beginPath();
c.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
c.strokeStyle = "blue";
c.stroke();
};
Update() {
if (this.x + this.r > pageWidth || this.x - this.r < 0) {
this.vx = -this.vx;
}
if (this.y + this.r > pageHeight || this.y - this.r < 0) {
this.vy = -this.vy;
}
//add velocity
this.x += this.vx;
this.y += this.vy;
this.draw();
};
}
for (var i = 0; i < 50; i++) {
var x = Math.random() * canvas.width;
var y = Math.random() * canvas.height;
var vx = Math.random();
var vy = Math.random();
var radius = 30;
let star = new Star(x, y, vx, vy, radius);
starArr.push(star);
}
console.log(starArr);
function animate() {
"use strict";
window.requestAnimationFrame(animate);
c.clearRect(0, 0, pageWidth, pageHeight);
for (var j = 0; j < starArr.length; j++); {
starArr[j].Update();
}
}
animate();
You have a typo at the for loop that calls the Update method: an extra semicolon ;
for (var j = 0; j < starArr.length; j++); {
const canvas = document.querySelector("canvas");
const c = canvas.getContext("2d");
let pageWidth = canvas.width = window.innerWidth;
let pageHeight = canvas.height = window.innerHeight;
let starArr = []
//Circle object
class Star{
constructor(X, Y, VX, VY, R){
this.x = X;
this.y = Y;
this.vx = VX;
this.vy = VY;
this.r = R;
}
draw() {
c.beginPath();
c.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
c.strokeStyle = "blue";
c.stroke();
};
Update() {
if (this.x + this.r > pageWidth || this.x - this.r < 0) {
this.vx = -this.vx;
}
if (this.y + this.r > pageHeight || this.y - this.r < 0) {
this.vy = -this.vy;
}
//add velocity
this.x += this.vx;
this.y += this.vy;
this.draw();
};
}
for (var i = 0; i < 50; i++) {
var x = Math.random() * canvas.width;
var y = Math.random() * canvas.height;
var vx = Math.random();
var vy = Math.random();
var radius = 30;
let star = new Star(x, y, vx, vy, radius);
starArr.push(star);
}
function animate() {
//"use strict";
window.requestAnimationFrame(animate);
c.clearRect(0, 0, pageWidth, pageHeight);
for (var j = 0; j < starArr.length; j++) {
starArr[j].Update();
}
}
animate();
<canvas></canvas>
i have made a an animations in canvas html but now i want to make the origin from where the balls originate move around the canvas according to my mouse position. i want to add mouse event function but i can't seem to get the logic straightand add added to the code , any help would be appreciated !
function runParticles () {
var canvas = document.createElement("canvas");
c = canvas.getContext("2d");
var particles = {};
var particleIndex = 0;
var particleNum = 15;
// set canvas size
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// add canvas to body
document.body.appendChild(canvas);
// style the canvas
c.fillStyle = "black";
c.fillRect(0, 0, canvas.width, canvas.height);
function Particle() {
this.x = canvas.width / 2;
this.y = canvas.height / 2;
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
this.gravity = 0.3;
particleIndex++;
particles[particleIndex] = this;
this.id = particleIndex;
this.life = 0;
this.maxLife = Math.random() * 30 + 60;
this.color = "hsla(" + parseInt(Math.random() * 360, 10) + ",90%,60%,0.5";
}
Particle.prototype.draw = function() {
this.x += this.vx;
this.y += this.vy;
if (Math.random() < 0.1) {
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
}
this.life++;
if (this.life >= this.maxLife) {
delete particles[this.id];
}
c.fillStyle = this.color;
//c.fillRect(this.x, this.y, 5, 10);
c.beginPath();
c.arc(this.x, this.y, 2.5, degToRad(0), degToRad(360));
c.fill();
};
setInterval(function() {
//normal setting before drawing over canvas w/ black background
c.globalCompositeOperation = "source-over";
c.fillStyle = "rgba(0,0,0,0.5)";
c.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < particleNum; i++) {
new Particle();
}
// c.globalCompositeOperation = "darken";
for (var i in particles) {
particles[i].draw();
}
}, 30);
}
function degToRad(deg) {
var radians = (deg * Math.PI / 180) - Math.PI / 2;
return radians;
}
<!DOCTYPE html5>
<html>
<head>
<title>disturbed</title>
<script src="toto.js" type="text/javascript"></script>
<script>
window.onload = () => runParticles();
</script>
</head>
<body>
</body>
</html>
I've added a function to detect the mouse position:
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
I've declared a variable m (the mouse).
var m = {x:canvas.width/2,y:canvas.height/2};
and I've changed the origin of the particles from this.x = canvas.width / 2;
this.y = canvas.height / 2;to this.x = m.x; this.y = m.y;
This is the position when the mouse do not move over the canvas
I've added an event "mousemove". When the mouse move over the canvas it's position change.
canvas.addEventListener("mousemove", (evt)=>{
m = oMousePos(canvas, evt);
})
I've also added an event "mouseleave". When the mouse leaves the canvas, the mouse goes back in the center.
canvas.addEventListener("mouseleave", (evt)=>{
m = {x:canvas.width/2,y:canvas.height/2};
})
Also I've changed the setInterval for requestAnimationFrame ( much more efficient ). The code inside is your code.
function runParticles () {
var canvas = document.createElement("canvas");
c = canvas.getContext("2d");
var particles = {};
var particleIndex = 0;
var particleNum = 8;
// set canvas size
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var m = {x:canvas.width/2,y:canvas.height/2};///////
// add canvas to body
document.body.appendChild(canvas);
// style the canvas
c.fillStyle = "black";
c.fillRect(0, 0, canvas.width, canvas.height);
function Particle() {
this.x = m.x;//////////
this.y = m.y;//////////
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
this.gravity = 0.3;
particleIndex++;
particles[particleIndex] = this;
this.id = particleIndex;
this.life = 0;
this.maxLife = Math.random() * 30 + 60;
this.color = "hsla(" + parseInt(Math.random() * 360, 10) + ",90%,60%,0.5";
}
Particle.prototype.draw = function() {
this.x += this.vx;
this.y += this.vy;
if (Math.random() < 0.1) {
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
}
this.life++;
if (this.life >= this.maxLife) {
delete particles[this.id];
}
c.fillStyle = this.color;
//c.fillRect(this.x, this.y, 5, 10);
c.beginPath();
c.arc(this.x, this.y, 2.5, degToRad(0), degToRad(360));
c.fill();
};
function Draw() {
window.requestAnimationFrame(Draw);
c.globalCompositeOperation = "source-over";
c.fillStyle = "rgba(0,0,0,0.5)";
c.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < particleNum; i++) {
new Particle();
}
// c.globalCompositeOperation = "darken";
for (var i in particles) {
particles[i].draw();
}
}
Draw();
///////////////////
canvas.addEventListener("mousemove", (evt)=>{
m = oMousePos(canvas, evt);
})
canvas.addEventListener("mouseleave", (evt)=>{
m = {x:canvas.width/2,y:canvas.height/2};
})
///////////////////
}
function degToRad(deg) {
var radians = (deg * Math.PI / 180) - Math.PI / 2;
return radians;
}
runParticles();
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
body,canvas{margin:0;padding:0;}
I hope this helps.
I'm working on a JQuery / JavaScript canvas brick break game, so I added the collision detection code if the ball hits the paddle or not, but when I ran the code, the collision detection code didn't work, I was wondering if anyone could help me? Here's the code.
JSFiddle: https://jsfiddle.net/18h7b9fd/
Main.js
$(document).ready(() => {
let fps = 60;
setInterval(init, 1000 / fps);
$(canvas).bind("mousemove", paddleControl);
})
// INIT
let init = () => {
draw(); //draw objects
animate(); //animate objects
collision(); //detect collision
}
// DRAW
let draw = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
paddle.draw();
ball.draw();
}
// ANIMATE
let animate = () => {
ball.animate();
}
// COLLISION
let collision = () => {
ball.collision();
}
// CONTROL
let paddleControl = (e) => {
// get mouse pos
let rect = canvas.getBoundingClientRect();
let root = document.documentElement;
let mouseX = e.pageX - rect.left - root.scrollLeft;
paddle.x = mouseX - paddle.w / 2;
}
Objects.js
class Paddle {
constructor(x, y, w, h, color) {
this.x = canvas.width / 2 - 100 / 2;
this.y = canvas.height - 60;
this.w = 100;
this.h = 10;
this.color = "#fff";
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
}
class Ball {
constructor(x, y, r, color, speedX, speedY) {
this.x = canvas.width / 2 - 10 / 2;
this.y = canvas.height / 2 - 10 / 2;
this.r = 10;
this.color = "#fff";
this.speedX = 6;
this.speedY = 6;
}
draw() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill();
}
animate() {
this.x += this.speedX;
this.y += this.speedY;
}
collision() {
// BALL TO PADDLE
let paddleTop = paddle.y - paddle.h;
let paddleBottom = canvas.height - paddleTop;
let paddleLeft = paddle.x;
let paddleRight = paddle.x + paddle.w;
if(this.y >= paddleTop && this.y <= paddleBottom &&
this.x >= paddleLeft && this.x <= paddleRight) {
this.speedY *= -1;
}
// BALL TO WALL
if(this.x >= canvas.width) this.speedX *= -1; //left
if(this.x <= 0) this.speedX *= -1; //right
if(this.y >= canvas.height) this.reset(); //bottom
if(this.y <= 0) this.speedY *= -1; //top
}
reset() {
this.x = canvas.width / 2 - this.r / 2;
this.y = canvas.height / 2 - this.r / 2;
this.animate();
}
}
let paddle = new Paddle(this.x, this.y, this.w, this.h, this.color);
let ball = new Ball(this.x, this.y, this.r, this.color, this.speedX, this.speedY);
console.log(paddle);
console.log(ball);
The problem is the way you are setting the paddle top and bottom. Use this instead:
let paddleTop = paddle.y;
let paddleBottom = paddleTop + paddle.h;
Currently the way you are setting the values makes it impossible for the condition to ever be true (as paddle bottom is always less than paddle top).
I have put together a fiddle here: https://jsfiddle.net/gegbqv5p/
You will also notice that I didn't use jQuery. For what you have so far, it really isn't necessary.