Apply random rotation to canvas particles - javascript

I'm trying to update an existing particle animation. In this demo, the particles are just lines with different lineWidths with random sizes and rotation as they fall down.
My goal is to replace the lines with a different shape, which looks like the image below keeping everything else as is.
I've already changed the shape, but I'm having a couple issues:
It doesn't rotate anymore like in the original demo link posted above.
Since, I replaced the lines with an image of the shape, I'm also facing performance issues if I increase the particles.
If I randomize the size, it keeps updating the size of the shape constantly without keeping the first random size.
context.drawImage(svg, x, y, 20, 40)
|
v
context.drawImage(svg, x, y, Math.random() * 20, Math.random() * 40)
Could anybody point me in the right direction on I should go about fixing the above. Appreciate any help!
var confetti = {
maxCount: 150, //set max confetti count
speed: 1, //set the particle animation speed
frameInterval: 30, //the confetti animation frame interval in milliseconds
alpha: 1.0, //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
gradient: false, //whether to use gradients for the confetti particles
start: null, //call to start confetti animation (with optional timeout in milliseconds, and optional min and max random confetti count)
stop: null, //call to stop adding confetti
toggle: null, //call to start or stop the confetti animation depending on whether it's already running
pause: null, //call to freeze confetti animation
resume: null, //call to unfreeze confetti animation
togglePause: null, //call to toggle whether the confetti animation is paused
remove: null, //call to stop the confetti animation and remove all confetti immediately
isPaused: null, //call and returns true or false depending on whether the confetti animation is paused
isRunning: null //call and returns true or false depending on whether the animation is running
};
(function() {
confetti.start = startConfetti;
confetti.stop = stopConfetti;
confetti.toggle = toggleConfetti;
confetti.pause = pauseConfetti;
confetti.resume = resumeConfetti;
confetti.togglePause = toggleConfettiPause;
confetti.isPaused = isConfettiPaused;
confetti.remove = removeConfetti;
confetti.isRunning = isConfettiRunning;
var supportsAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
var colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,", "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,", "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,", "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"];
var streamingConfetti = false;
var animationTimer = null;
var pause = false;
var lastFrameTime = Date.now();
var particles = [];
var waveAngle = 0;
var context = null;
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function resetParticle(particle, width, height) {
particle.color = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
particle.color2 = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
particle.x = getRandomInt(0, width);
particle.y = getRandomInt(-height / 2, 0);
particle.diameter = Math.random() * 10 + 5;
particle.tilt = Math.random() * 10 - 10;
particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
particle.tiltAngle = Math.random() * Math.PI;
return particle;
}
function toggleConfettiPause() {
if (pause)
resumeConfetti();
else
pauseConfetti();
}
function isConfettiPaused() {
return pause;
}
function pauseConfetti() {
pause = true;
}
function resumeConfetti() {
pause = false;
runAnimation();
}
function runAnimation() {
if (pause)
return;
else if (particles.length === 0) {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
animationTimer = null;
} else {
var now = Date.now();
var delta = now - lastFrameTime;
if (!supportsAnimationFrame || delta > confetti.frameInterval) {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
updateParticles();
drawParticles(context);
lastFrameTime = now - (delta % confetti.frameInterval);
}
animationTimer = requestAnimationFrame(runAnimation);
}
}
function startConfetti(timeout, min, max) {
var width = window.innerWidth;
var height = window.innerHeight;
window.requestAnimationFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
return window.setTimeout(callback, confetti.frameInterval);
};
})();
var canvas = document.getElementById("confetti-canvas");
if (canvas === null) {
canvas = document.createElement("canvas");
canvas.setAttribute("id", "confetti-canvas");
canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0");
document.body.prepend(canvas);
canvas.width = width;
canvas.height = height;
window.addEventListener("resize", function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}, true);
context = canvas.getContext("2d");
} else if (context === null)
context = canvas.getContext("2d");
var count = confetti.maxCount;
if (min) {
if (max) {
if (min == max)
count = particles.length + max;
else {
if (min > max) {
var temp = min;
min = max;
max = temp;
}
count = particles.length + ((Math.random() * (max - min) + min) | 0);
}
} else
count = particles.length + min;
} else if (max)
count = particles.length + max;
while (particles.length < count)
particles.push(resetParticle({}, width, height));
streamingConfetti = true;
pause = false;
runAnimation();
if (timeout) {
window.setTimeout(stopConfetti, timeout);
}
}
function stopConfetti() {
streamingConfetti = false;
}
function removeConfetti() {
stop();
pause = false;
particles = [];
}
function toggleConfetti() {
if (streamingConfetti)
stopConfetti();
else
startConfetti();
}
function isConfettiRunning() {
return streamingConfetti;
}
function drawParticles(context) {
var particle;
var x, y, x2, y2;
for (var i = 0; i < particles.length; i++) {
particle = particles[i];
particleWidth = particle.diameter;
x2 = particle.x + particle.tilt;
x = x2 + particle.diameter / 2;
y = particle.y;
var svg = new Image();
svg.src = 'https://i.postimg.cc/TPBmVXH1/confetti.png';
context.drawImage(svg, x, y, 20, 40)
/*context.beginPath();
context.lineWidth = particle.diameter;
x2 = particle.x + particle.tilt;
x = x2 + particle.diameter / 2;
y2 = particle.y + particle.tilt + particle.diameter / 2;
if (confetti.gradient) {
var gradient = context.createLinearGradient(x, particle.y, x2, y2);
gradient.addColorStop("0", particle.color);
gradient.addColorStop("1.0", particle.color2);
context.strokeStyle = gradient;
} else
context.strokeStyle = particle.color;
context.moveTo(x, particle.y);
context.lineTo(x2, y2);
context.stroke();*/
}
}
function updateParticles() {
var width = window.innerWidth;
var height = window.innerHeight;
var particle;
waveAngle += 0.01;
for (var i = 0; i < particles.length; i++) {
particle = particles[i];
if (!streamingConfetti && particle.y < -15)
particle.y = height + 100;
else {
particle.tiltAngle += particle.tiltAngleIncrement;
particle.x += Math.sin(waveAngle) - 0.5;
particle.y += (Math.cos(waveAngle) + particle.diameter + confetti.speed) * 0.5;
particle.tilt = Math.sin(particle.tiltAngle) * 15;
}
if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
if (streamingConfetti && particles.length <= confetti.maxCount)
resetParticle(particle, width, height);
else {
particles.splice(i, 1);
i--;
}
}
}
}
startConfetti(5000, 20, 25)
})();
html {
height: 100%;
}
body,
html {
margin: 0;
}
body {
background: black;
}

Ok so here is what I did:
I changed drawParticles to apply the particle.tilt.
I moved the svg variable to the top so it is reused and not loaded multiple times.
I did set the random size in resetParticle so it does not change afterwards.
var confetti = {
maxCount: 150, //set max confetti count
speed: 1, //set the particle animation speed
frameInterval: 30, //the confetti animation frame interval in milliseconds
alpha: 1.0, //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
gradient: false, //whether to use gradients for the confetti particles
start: null, //call to start confetti animation (with optional timeout in milliseconds, and optional min and max random confetti count)
stop: null, //call to stop adding confetti
toggle: null, //call to start or stop the confetti animation depending on whether it's already running
pause: null, //call to freeze confetti animation
resume: null, //call to unfreeze confetti animation
togglePause: null, //call to toggle whether the confetti animation is paused
remove: null, //call to stop the confetti animation and remove all confetti immediately
isPaused: null, //call and returns true or false depending on whether the confetti animation is paused
isRunning: null //call and returns true or false depending on whether the animation is running
};
(function () {
confetti.start = startConfetti;
confetti.stop = stopConfetti;
confetti.toggle = toggleConfetti;
confetti.pause = pauseConfetti;
confetti.resume = resumeConfetti;
confetti.togglePause = toggleConfettiPause;
confetti.isPaused = isConfettiPaused;
confetti.remove = removeConfetti;
confetti.isRunning = isConfettiRunning;
var supportsAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
var colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,", "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,", "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,", "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"];
var streamingConfetti = false;
var animationTimer = null;
var pause = false;
var lastFrameTime = Date.now();
var particles = [];
var waveAngle = 0;
var context = null;
var sizes = [];
var svg = new Image();
svg.src = 'https://i.postimg.cc/TPBmVXH1/confetti.png';
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function resetParticle(particle, width, height) {
particle.color = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
particle.color2 = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
particle.x = getRandomInt(0, width);
particle.y = getRandomInt(-height / 2, 0);
particle.diameter = Math.random() * 10 + 5;
particle.tilt = Math.random() * 10 - 10;
particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
particle.tiltAngle = Math.random() * Math.PI;
particle.width = Math.random() * 20
particle.height = Math.random() * 40
return particle;
}
function toggleConfettiPause() {
if (pause)
resumeConfetti();
else
pauseConfetti();
}
function isConfettiPaused() {
return pause;
}
function pauseConfetti() {
pause = true;
}
function resumeConfetti() {
pause = false;
runAnimation();
}
function runAnimation() {
if (pause)
return;
else if (particles.length === 0) {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
animationTimer = null;
} else {
var now = Date.now();
var delta = now - lastFrameTime;
if (!supportsAnimationFrame || delta > confetti.frameInterval) {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
updateParticles();
drawParticles(context);
lastFrameTime = now - (delta % confetti.frameInterval);
}
animationTimer = requestAnimationFrame(runAnimation);
}
}
function startConfetti(timeout, min, max) {
var width = window.innerWidth;
var height = window.innerHeight;
window.requestAnimationFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, confetti.frameInterval);
};
})();
var canvas = document.getElementById("confetti-canvas");
if (canvas === null) {
canvas = document.createElement("canvas");
canvas.setAttribute("id", "confetti-canvas");
canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0");
document.body.prepend(canvas);
canvas.width = width;
canvas.height = height;
window.addEventListener("resize", function () {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}, true);
context = canvas.getContext("2d");
} else if (context === null)
context = canvas.getContext("2d");
var count = confetti.maxCount;
if (min) {
if (max) {
if (min == max)
count = particles.length + max;
else {
if (min > max) {
var temp = min;
min = max;
max = temp;
}
count = particles.length + ((Math.random() * (max - min) + min) | 0);
}
} else
count = particles.length + min;
} else if (max)
count = particles.length + max;
while (particles.length < count)
particles.push(resetParticle({}, width, height));
streamingConfetti = true;
pause = false;
runAnimation();
if (timeout) {
window.setTimeout(stopConfetti, timeout);
}
}
function stopConfetti() {
streamingConfetti = false;
}
function removeConfetti() {
stop();
pause = false;
particles = [];
}
function toggleConfetti() {
if (streamingConfetti)
stopConfetti();
else
startConfetti();
}
function isConfettiRunning() {
return streamingConfetti;
}
function drawParticles(context) {
var particle;
var x, y, x2, y2;
for (var i = 0; i < particles.length; i++) {
particle = particles[i];
particleWidth = particle.diameter;
x2 = particle.x + particle.tilt;
x = x2 + particle.diameter / 2;
y = particle.y;
context.save();
context.translate(x, y);
context.rotate(particle.tilt / 180 * Math.PI);
context.translate(-x, -y);
context.drawImage(svg, x, y, particle.width, particle.height);
context.restore();
/*context.beginPath();
context.lineWidth = particle.diameter;
x2 = particle.x + particle.tilt;
x = x2 + particle.diameter / 2;
y2 = particle.y + particle.tilt + particle.diameter / 2;
if (confetti.gradient) {
var gradient = context.createLinearGradient(x, particle.y, x2, y2);
gradient.addColorStop("0", particle.color);
gradient.addColorStop("1.0", particle.color2);
context.strokeStyle = gradient;
} else
context.strokeStyle = particle.color;
context.moveTo(x, particle.y);
context.lineTo(x2, y2);
context.stroke();*/
}
}
function updateParticles() {
var width = window.innerWidth;
var height = window.innerHeight;
var particle;
waveAngle += 0.01;
for (var i = 0; i < particles.length; i++) {
particle = particles[i];
if (!streamingConfetti && particle.y < -15)
particle.y = height + 100;
else {
particle.tiltAngle += particle.tiltAngleIncrement;
particle.x += Math.sin(waveAngle) - 0.5;
particle.y += (Math.cos(waveAngle) + particle.diameter + confetti.speed) * 0.5;
particle.tilt = Math.sin(particle.tiltAngle) * 15;
}
if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
if (streamingConfetti && particles.length <= confetti.maxCount)
resetParticle(particle, width, height);
else {
particles.splice(i, 1);
i--;
}
}
}
}
startConfetti(5000, 20, 25)
})();
html {
height: 100%;
}
body, html {
margin: 0;
}
body {
background: black;
}

Related

How do i make a border work on my website?

I am creating a website to wish my friend on their birthday. I want it to be like a card, there is text in the center and an image of a birthday cake. i want to create a border around it to make it look like a card. i know how to change flexbox values but i really dont want to mess up. Can someone provide me the solution?
var confetti = {
maxCount: 150, //set max confetti count
speed: 2, //set the particle animation speed
frameInterval: 15, //the confetti animation frame interval in milliseconds
alpha: 1.0, //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
gradient: false, //whether to use gradients for the confetti particles
start: null, //call to start confetti animation (with optional timeout in milliseconds, and optional min and max random confetti count)
stop: null, //call to stop adding confetti
toggle: null, //call to start or stop the confetti animation depending on whether it's already running
pause: null, //call to freeze confetti animation
resume: null, //call to unfreeze confetti animation
togglePause: null, //call to toggle whether the confetti animation is paused
remove: null, //call to stop the confetti animation and remove all confetti immediately
isPaused: null, //call and returns true or false depending on whether the confetti animation is paused
isRunning: null //call and returns true or false depending on whether the animation is running
};
(function() {
confetti.start = startConfetti;
confetti.stop = stopConfetti;
confetti.toggle = toggleConfetti;
confetti.pause = pauseConfetti;
confetti.resume = resumeConfetti;
confetti.togglePause = toggleConfettiPause;
confetti.isPaused = isConfettiPaused;
confetti.remove = removeConfetti;
confetti.isRunning = isConfettiRunning;
var supportsAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
var colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,", "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,", "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,", "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"];
var streamingConfetti = false;
var animationTimer = null;
var pause = false;
var lastFrameTime = Date.now();
var particles = [];
var waveAngle = 0;
var context = null;
function resetParticle(particle, width, height) {
particle.color = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
particle.color2 = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
particle.x = Math.random() * width;
particle.y = Math.random() * height - height;
particle.diameter = Math.random() * 10 + 5;
particle.tilt = Math.random() * 10 - 10;
particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
particle.tiltAngle = Math.random() * Math.PI;
return particle;
}
function toggleConfettiPause() {
if (pause)
resumeConfetti();
else
pauseConfetti();
}
function isConfettiPaused() {
return pause;
}
function pauseConfetti() {
pause = true;
}
function resumeConfetti() {
pause = false;
runAnimation();
}
function runAnimation() {
if (pause)
return;
else if (particles.length === 0) {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
animationTimer = null;
} else {
var now = Date.now();
var delta = now - lastFrameTime;
if (!supportsAnimationFrame || delta > confetti.frameInterval) {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
updateParticles();
drawParticles(context);
lastFrameTime = now - (delta % confetti.frameInterval);
}
animationTimer = requestAnimationFrame(runAnimation);
}
}
function startConfetti(timeout, min, max) {
var width = window.innerWidth;
var height = window.innerHeight;
window.requestAnimationFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, confetti.frameInterval);
};
})();
var canvas = document.getElementById("confetti-canvas");
if (canvas === null) {
canvas = document.createElement("canvas");
canvas.setAttribute("id", "confetti-canvas");
canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0");
document.body.prepend(canvas);
canvas.width = width;
canvas.height = height;
window.addEventListener("resize", function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}, true);
context = canvas.getContext("2d");
} else if (context === null)
context = canvas.getContext("2d");
var count = confetti.maxCount;
if (min) {
if (max) {
if (min == max)
count = particles.length + max;
else {
if (min > max) {
var temp = min;
min = max;
max = temp;
}
count = particles.length + ((Math.random() * (max - min) + min) | 0);
}
} else
count = particles.length + min;
} else if (max)
count = particles.length + max;
while (particles.length < count)
particles.push(resetParticle({}, width, height));
streamingConfetti = true;
pause = false;
runAnimation();
if (timeout) {
window.setTimeout(stopConfetti, timeout);
}
}
function stopConfetti() {
streamingConfetti = false;
}
function removeConfetti() {
stop();
pause = false;
particles = [];
}
function toggleConfetti() {
if (streamingConfetti)
stopConfetti();
else
startConfetti();
}
function isConfettiRunning() {
return streamingConfetti;
}
function drawParticles(context) {
var particle;
var x, y, x2, y2;
for (var i = 0; i < particles.length; i++) {
particle = particles[i];
context.beginPath();
context.lineWidth = particle.diameter;
x2 = particle.x + particle.tilt;
x = x2 + particle.diameter / 2;
y2 = particle.y + particle.tilt + particle.diameter / 2;
if (confetti.gradient) {
var gradient = context.createLinearGradient(x, particle.y, x2, y2);
gradient.addColorStop("0", particle.color);
gradient.addColorStop("1.0", particle.color2);
context.strokeStyle = gradient;
} else
context.strokeStyle = particle.color;
context.moveTo(x, particle.y);
context.lineTo(x2, y2);
context.stroke();
}
}
function updateParticles() {
var width = window.innerWidth;
var height = window.innerHeight;
var particle;
waveAngle += 0.01;
for (var i = 0; i < particles.length; i++) {
particle = particles[i];
if (!streamingConfetti && particle.y < -15)
particle.y = height + 100;
else {
particle.tiltAngle += particle.tiltAngleIncrement;
particle.x += Math.sin(waveAngle) - 0.5;
particle.y += (Math.cos(waveAngle) + particle.diameter + confetti.speed) * 0.5;
particle.tilt = Math.sin(particle.tiltAngle) * 15;
}
if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
if (streamingConfetti && particles.length <= confetti.maxCount)
resetParticle(particle, width, height);
else {
particles.splice(i, 1);
i--;
}
}
}
}
})();
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--emoji-->
<meta charset="UTF-8">
<!--title-->
<title>🎉Happy Birthday🎉</title>
<!--animation-->
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">
<!--favicon-->
<link rel="icon" type="image/x-icon" href="Images/favicon.ico">
<link href='https://fonts.googleapis.com/css?family=Raleway' rel='stylesheet'>
<!--CSS-->
<style>
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
</style>
</head>
<body style="background-color:hsl(0, 0%, 0%); width: 100%; height: 100%;">
<div class="animated pulse infinite" style="font-size: 90px; color:#06589c ; text-shadow: 0 0 2rem #06589c; font-family:'Raleway'; text-align: center; margin-top: 55px; font-weight: bold;">Happy Birthday Eshal!</div>
<div class="Birthday Cake" style="text-align: center;">
<img src="images/favicon.png" width="346.4px" height="310.8px" alt="Birthday Cake" style="text-shadow: 0 0 2rem white;">
</div>
<script src="confetti.js"></script>
<!-- Confetti JS-->
<script>
// start
const start = () => {
setTimeout(function() {
confetti.start()
}, 1000); // 1000 is time that after 1 second start the confetti ( 1000 = 1 sec)
};
// Stop
const stop = () => {
setTimeout(function() {
confetti.stop()
}, 5000); // 5000 is time that after 5 second stop the confetti ( 5000 = 5 sec)
};
start();
stop();
</script>
</body>
</html>
There is many ways to do.
Here is one way to do by adding it on html directly, but I don't know what border type you are trying to set.
html{
border: 5px solid white;
margin: 10px;
min-height: calc(100vh - 20px);
}
body{
overflow-x:hidden;
}
DEMO
var confetti = {
maxCount: 150, //set max confetti count
speed: 2, //set the particle animation speed
frameInterval: 15, //the confetti animation frame interval in milliseconds
alpha: 1.0, //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
gradient: false, //whether to use gradients for the confetti particles
start: null, //call to start confetti animation (with optional timeout in milliseconds, and optional min and max random confetti count)
stop: null, //call to stop adding confetti
toggle: null, //call to start or stop the confetti animation depending on whether it's already running
pause: null, //call to freeze confetti animation
resume: null, //call to unfreeze confetti animation
togglePause: null, //call to toggle whether the confetti animation is paused
remove: null, //call to stop the confetti animation and remove all confetti immediately
isPaused: null, //call and returns true or false depending on whether the confetti animation is paused
isRunning: null //call and returns true or false depending on whether the animation is running
};
(function() {
confetti.start = startConfetti;
confetti.stop = stopConfetti;
confetti.toggle = toggleConfetti;
confetti.pause = pauseConfetti;
confetti.resume = resumeConfetti;
confetti.togglePause = toggleConfettiPause;
confetti.isPaused = isConfettiPaused;
confetti.remove = removeConfetti;
confetti.isRunning = isConfettiRunning;
var supportsAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
var colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,", "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,", "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,", "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"];
var streamingConfetti = false;
var animationTimer = null;
var pause = false;
var lastFrameTime = Date.now();
var particles = [];
var waveAngle = 0;
var context = null;
function resetParticle(particle, width, height) {
particle.color = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
particle.color2 = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
particle.x = Math.random() * width;
particle.y = Math.random() * height - height;
particle.diameter = Math.random() * 10 + 5;
particle.tilt = Math.random() * 10 - 10;
particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
particle.tiltAngle = Math.random() * Math.PI;
return particle;
}
function toggleConfettiPause() {
if (pause)
resumeConfetti();
else
pauseConfetti();
}
function isConfettiPaused() {
return pause;
}
function pauseConfetti() {
pause = true;
}
function resumeConfetti() {
pause = false;
runAnimation();
}
function runAnimation() {
if (pause)
return;
else if (particles.length === 0) {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
animationTimer = null;
} else {
var now = Date.now();
var delta = now - lastFrameTime;
if (!supportsAnimationFrame || delta > confetti.frameInterval) {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
updateParticles();
drawParticles(context);
lastFrameTime = now - (delta % confetti.frameInterval);
}
animationTimer = requestAnimationFrame(runAnimation);
}
}
function startConfetti(timeout, min, max) {
var width = window.innerWidth;
var height = window.innerHeight;
window.requestAnimationFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, confetti.frameInterval);
};
})();
var canvas = document.getElementById("confetti-canvas");
if (canvas === null) {
canvas = document.createElement("canvas");
canvas.setAttribute("id", "confetti-canvas");
canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0");
document.body.prepend(canvas);
canvas.width = width;
canvas.height = height;
window.addEventListener("resize", function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}, true);
context = canvas.getContext("2d");
} else if (context === null)
context = canvas.getContext("2d");
var count = confetti.maxCount;
if (min) {
if (max) {
if (min == max)
count = particles.length + max;
else {
if (min > max) {
var temp = min;
min = max;
max = temp;
}
count = particles.length + ((Math.random() * (max - min) + min) | 0);
}
} else
count = particles.length + min;
} else if (max)
count = particles.length + max;
while (particles.length < count)
particles.push(resetParticle({}, width, height));
streamingConfetti = true;
pause = false;
runAnimation();
if (timeout) {
window.setTimeout(stopConfetti, timeout);
}
}
function stopConfetti() {
streamingConfetti = false;
}
function removeConfetti() {
stop();
pause = false;
particles = [];
}
function toggleConfetti() {
if (streamingConfetti)
stopConfetti();
else
startConfetti();
}
function isConfettiRunning() {
return streamingConfetti;
}
function drawParticles(context) {
var particle;
var x, y, x2, y2;
for (var i = 0; i < particles.length; i++) {
particle = particles[i];
context.beginPath();
context.lineWidth = particle.diameter;
x2 = particle.x + particle.tilt;
x = x2 + particle.diameter / 2;
y2 = particle.y + particle.tilt + particle.diameter / 2;
if (confetti.gradient) {
var gradient = context.createLinearGradient(x, particle.y, x2, y2);
gradient.addColorStop("0", particle.color);
gradient.addColorStop("1.0", particle.color2);
context.strokeStyle = gradient;
} else
context.strokeStyle = particle.color;
context.moveTo(x, particle.y);
context.lineTo(x2, y2);
context.stroke();
}
}
function updateParticles() {
var width = window.innerWidth;
var height = window.innerHeight;
var particle;
waveAngle += 0.01;
for (var i = 0; i < particles.length; i++) {
particle = particles[i];
if (!streamingConfetti && particle.y < -15)
particle.y = height + 100;
else {
particle.tiltAngle += particle.tiltAngleIncrement;
particle.x += Math.sin(waveAngle) - 0.5;
particle.y += (Math.cos(waveAngle) + particle.diameter + confetti.speed) * 0.5;
particle.tilt = Math.sin(particle.tiltAngle) * 15;
}
if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
if (streamingConfetti && particles.length <= confetti.maxCount)
resetParticle(particle, width, height);
else {
particles.splice(i, 1);
i--;
}
}
}
}
})();
html{
border: 5px solid white;
margin: 10px;
min-height: calc(100vh - 20px);
}
body{
overflow-x:hidden;
}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--emoji-->
<meta charset="UTF-8">
<!--title-->
<title>🎉Happy Birthday🎉</title>
<!--animation-->
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">
<!--favicon-->
<link rel="icon" type="image/x-icon" href="Images/favicon.ico">
<link href='https://fonts.googleapis.com/css?family=Raleway' rel='stylesheet'>
<!--CSS-->
<style>
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
</style>
</head>
<body style="background-color:hsl(0, 0%, 0%); width: 100%; height: 100%;">
<div class="animated pulse infinite" style="font-size: 90px; color:#06589c ; text-shadow: 0 0 2rem #06589c; font-family:'Raleway'; text-align: center; margin-top: 55px; font-weight: bold;">Happy Birthday Eshal!</div>
<div class="Birthday Cake" style="text-align: center;">
<img src="images/favicon.png" width="346.4px" height="310.8px" alt="Birthday Cake" style="text-shadow: 0 0 2rem white;">
</div>
<script src="confetti.js"></script>
<!-- Confetti JS-->
<script>
// start
const start = () => {
setTimeout(function() {
confetti.start()
}, 1000); // 1000 is time that after 1 second start the confetti ( 1000 = 1 sec)
};
// Stop
const stop = () => {
setTimeout(function() {
confetti.stop()
}, 5000); // 5000 is time that after 5 second stop the confetti ( 5000 = 5 sec)
};
start();
stop();
</script>
</body>
</html>

How to migrate a legacy a DOMContentLoaded function inside a Vue 3 component

I'm trying to refactor a website inside a Vue 3 single page app.
Everything is working but I cannot understand which is the best way to insert and execute some javascript functions...
In my homepage, that now is a component, I use this function to create a parallax star + mountain landscape
document.addEventListener('DOMContentLoaded', () => {
(function() {
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
window.setTimeout(callback, 1000 / 60);
};
window.requestAnimationFrame = requestAnimationFrame;
})();
// Terrain stuff.
var background = document.getElementById("bg-canvas")
var backgroundHome = document.getElementById("bg-home")
var bgCtx = background.getContext("2d"),
width = window.innerWidth,
height = window.innerHeight;
(height < 400) ? height = 400: height;
background.width = width;
background.height = height;
function Terrain(options) {
options = options || {};
this.terrain = document.createElement("canvas");
this.terCtx = this.terrain.getContext("2d");
this.scrollDelay = options.scrollDelay || 90;
this.lastScroll = new Date().getTime();
this.terrain.width = width;
this.terrain.height = height;
this.fillStyle = options.fillStyle || "#494776";
this.mHeight = options.mHeight || height;
// generate
this.points = [];
var displacement = options.displacement || 140,
power = Math.pow(2, Math.ceil(Math.log(width) / (Math.log(2))));
// set the start height and end height for the terrain
this.points[0] = this.mHeight; //(this.mHeight - (Math.random() * this.mHeight / 2)) - displacement;
this.points[power] = this.points[0];
// create the rest of the points
for (var i = 1; i < power; i *= 2) {
for (var j = (power / i) / 2; j < power; j += power / i) {
this.points[j] = ((this.points[j - (power / i) / 2] + this.points[j + (power / i) / 2]) / 2) + Math.floor(Math.random() * -displacement + displacement);
}
displacement *= 0.6;
}
background.after(this.terrain);
}
Terrain.prototype.update = function() {
// draw the terrain
this.terCtx.clearRect(0, 0, width, height);
this.terCtx.fillStyle = this.fillStyle;
if (new Date().getTime() > this.lastScroll + this.scrollDelay) {
this.lastScroll = new Date().getTime();
this.points.push(this.points.shift());
}
this.terCtx.beginPath();
for (var i = 0; i <= width; i++) {
if (i === 0) {
this.terCtx.moveTo(0, this.points[0]);
} else if (this.points[i] !== undefined) {
this.terCtx.lineTo(i, this.points[i]);
}
}
this.terCtx.lineTo(width, this.terrain.height);
this.terCtx.lineTo(0, this.terrain.height);
this.terCtx.lineTo(0, this.points[0]);
this.terCtx.fill();
}
// Second canvas used for the stars
bgCtx.fillStyle = '#05004c';
bgCtx.fillRect(0, 0, width, height);
// stars
function Star(options) {
this.size = Math.random() * 2;
this.speed = Math.random() * .05;
this.x = options.x;
this.y = options.y;
}
Star.prototype.reset = function() {
this.size = Math.random() * 2;
this.speed = Math.random() * .05;
this.x = width;
this.y = Math.random() * height;
}
Star.prototype.update = function() {
this.x -= this.speed;
if (this.x < 0) {
this.reset();
} else {
bgCtx.fillRect(this.x, this.y, this.size, this.size);
}
}
function ShootingStar() {
this.reset();
}
ShootingStar.prototype.reset = function() {
this.x = Math.random() * width;
this.y = 0;
this.len = (Math.random() * 80) + 10;
this.speed = (Math.random() * 10) + 6;
this.size = (Math.random() * 1) + 0.1;
// this is used so the shooting stars arent constant
this.waitTime = new Date().getTime() + (Math.random() * 3000) + 500;
this.active = false;
}
ShootingStar.prototype.update = function() {
if (this.active) {
this.x -= this.speed;
this.y += this.speed;
if (this.x < 0 || this.y >= height) {
this.reset();
} else {
bgCtx.lineWidth = this.size;
bgCtx.beginPath();
bgCtx.moveTo(this.x, this.y);
bgCtx.lineTo(this.x + this.len, this.y - this.len);
bgCtx.stroke();
}
} else {
if (this.waitTime < new Date().getTime()) {
this.active = true;
}
}
}
var entities = [];
// init the stars
for (var i = 0; i < height; i++) {
entities.push(new Star({
x: Math.random() * width,
y: Math.random() * height
}));
}
// Add 2 shooting stars that just cycle.
entities.push(new ShootingStar());
entities.push(new ShootingStar());
entities.push(new Terrain({
displacement: 150,
scrollDelay: 20,
fillStyle: "#221445",
mHeight: height / 2
}));
entities.push(new Terrain({
displacement: 120,
scrollDelay: 50,
fillStyle: "#2e1f56",
mHeight: (height / 2) - 30
}));
entities.push(new Terrain({
mHeight: (height / 2) - 60
}));
//animate background
function animate() {
bgCtx.fillStyle = '#055e8f';
bgCtx.fillRect(0, 0, width, height);
bgCtx.fillStyle = '#ffffff';
bgCtx.strokeStyle = '#ffffff';
var entLen = entities.length;
while (entLen--) {
entities[entLen].update();
}
requestAnimationFrame(animate);
}
animate();
});
Which is the most efficient way to execute this when my homepage component is mounted?

JavaScript Canvas moving and animating color at the same time

My script is animating the changing of the color of the ball. The 1 complete sequence takes 1 second and changes the color from green to blue using gradient. I am trying to add a move() method to move the ball.
However I have a problem where to execute my move() method.
Right now the animation of the ball moving is executed witihin the animation of the color but it shouldn't be. The color should change diffrently and the ball should be moving ininitely. I want the ball to move infinitely and the chaning of the color has to take 1 second like it is now.
Am I doing this right?
<-- Edit - I changed interval to 2000ms, just to visualize the problem better ( the moving of the ball stops but it shouldn't)
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var i = 0;
var ec;
var direction = 0;
var x = 100;
var dx = 10;
var y = 100;
var radius = 100;
function move() {
if(x + radius > window.innerWidth || x - radius < 0) {
dx = -dx;
}
x += dx;
}
function animate() {
c.clearRect(0, 0, innerWidth, innerHeight)
move();
var gradient = c.createLinearGradient(0, 0, 170, 0);
ec = 255 - i;
gradient.addColorStop(1, "rgba(" + 0 + "," + i + "," + ec + ")", 1);
c.fillStyle = gradient;
c.beginPath();
c.arc(x, y, radius, 0, 2 * Math.PI);
c.fill();
console.log(i, ec);
if(i == 255) direction = 1;
if(direction == 1 && i == 0) direction = 2;
if(direction == 0 ) i+=5;
else if(direction == 1 && direction != 2) i -= 5;
if(direction != 2) requestAnimationFrame(animate);
}
setInterval(function draw() {
direction = 0;
animate();
}, 2000);
canvas {
border: 1px solid black;
}
body {
margin: 0;
}
<canvas></canvas>
I got everything working as desired. But it was complicated, is there any way to make it simpler?
Could someone teach me how to use snippet?
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var i = 0;
var ec;
var direction = 0;
var x = 300;
var y = 300;
var radius = 200;
var speed = 5;
var dateDiff;
var date1;
var executedTimer = false;
var executedColor = false;
function draw() {
c.clearRect(0, 0, innerWidth, innerHeight)
c.beginPath();
c.arc(x, y, radius, 0, 2 * Math.PI);
c.fill();
}
function move() {
if(x + radius > window.innerWidth || x - radius < 0) {
speed = -speed;
}
x += speed;
}
function color() {
var gradient = c.createLinearGradient(0, 0, 170, 0);
ec = 255 - i;
gradient.addColorStop(1, "rgba(" + 0 + "," + i + "," + ec + ")", 1);
c.fillStyle = gradient;
//console.log(i, ec);
if(i == 255) direction = 1;
if(direction == 1 && i == 0) direction = 2;
if(direction == 0 ) i += 5;
else if(direction == 1 && direction != 2) i -= 5;
}
function timerStart() {
date1 = new Date();
executedTimer = true;
}
function timerCheck() {
var date2 = new Date();
dateDiff = Math.abs(date1 - date2);
if(dateDiff >= 1000)date1 = date2;
}
function animate() {
draw();
move();
if(executedTimer == false) timerStart();
if(executedColor == false) {
executedColor = true;
color();
}
if(dateDiff < 1000) color();
if(dateDiff >= 1000 && direction == 2) {
i = 0;
direction = 0;
//console.log(dateDiff);
color();
}
timerCheck();
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
canvas {
border: 1px solid black;
}
body {
margin: 0;
}
<canvas></canvas>
Treat both the position and the color as two different components, each with its own duration and its update method.
Then start a single animation loop in which you will
update the color component
update the position component
draw the scene
To ensure your animations last the duration you want, use a delta-time, which will let you know where you are relatively to the beginning of the animation.
This avoids issues with monitor using different refresh-rates (simply incrementing a value by a fixed amount will make your objects move twice faster on a 120Hz monitor than on a 60Hz one, generally not what you want), and it also allows to not care about the fact your animation is being paused by the browser when offscreen.
{
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const w = canvas.width = window.innerWidth;
const h = canvas.height = window.innerHeight;
const radius = Math.min( w, h ) / 3;
const color = {
duration: 1000,
update( elapsed ) {
const delta = (elapsed % this.duration) / this.duration;
const direction = (elapsed % (this.duration * 2)) - this.duration;
const val = (direction < 0) ?
255 - (delta * 255) :
delta * 255 ;
this.val = `rgba(0, ${ val }, ${ 255 - val })`;
}
};
const position = {
duration: 3000,
y: h / 2,
update( elapsed ) {
const delta = (elapsed % this.duration) / this.duration;
const direction = (elapsed % (this.duration * 2)) - this.duration;
this.x = direction < 0 ?
delta * w :
w - (delta * w);
}
};
function draw() {
ctx.clearRect( 0, 0, w, h );
ctx.beginPath();
ctx.arc( position.x, position.y, radius, 0, Math.PI *2 );
ctx.fillStyle = color.val;
ctx.fill();
}
// We record the time we started the animation
// We will base our time relative animation on that
const start = performance.now();
function anim( timestamp ) {
const elapsed = timestamp - start;
color.update( elapsed );
position.update( elapsed );
draw();
requestAnimationFrame( anim );
}
requestAnimationFrame( anim );
}
body {
margin: 0;
}
<canvas></canvas>
And of course if you are going to have a lot of such both-directions animations, you could write an helper function to just get the value based on the elapsed time:
{
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const w = canvas.width = window.innerWidth;
const h = canvas.height = window.innerHeight;
const radius = Math.min( w, h ) / 3;
function getValueAtTime( elapsed, duration, max_val ) {
const delta = (elapsed % duration) / duration;
const direction = (elapsed % (duration * 2)) - duration;
const val = delta * max_val;
return direction < 0 ? val : max_val - val;
}
const color = {
duration: 1000,
update( elapsed ) {
const val = getValueAtTime( elapsed, this.duration, 255 );
this.val = `rgba(0, ${ val }, ${ 255 - val })`;
}
};
const position = {
duration: 3000,
y: h / 2,
update( elapsed ) {
this.x = getValueAtTime( elapsed, this.duration, w );
}
};
function draw() {
ctx.clearRect( 0, 0, w, h );
ctx.beginPath();
ctx.arc( position.x, position.y, radius, 0, Math.PI *2 );
ctx.fillStyle = color.val;
ctx.fill();
}
// We record the time we started the animation
// We will base our time relative animation on that
const start = performance.now();
function anim( timestamp ) {
const elapsed = timestamp - start;
color.update( elapsed );
position.update( elapsed );
draw();
requestAnimationFrame( anim );
}
requestAnimationFrame( anim );
}
body {
margin: 0;
}
<canvas></canvas>

How can I stop this canvas animation drawing new particles after 5 seconds and restart it from zero particles on click?

I would like to stop drawing new particles after 5 seconds and restart drawing on click anywhere.
Here is the code https://codepen.io/Jeremboo/pen/ENVaMY for the animation.
Right now it keeps on drawing until you click on the screen to restart it again.
What I want is to stop drawing after 5 seconds and then on click anywhere restart it from zero particles. I tried the following to stop but I am not able to start it again.
this is the js code
/**/ const canvas = document.createElement('canvas');
/**/ const context = canvas.getContext('2d');
/**/ let windowWidth = canvas.width = window.innerWidth;
/**/ let windowHeight = canvas.height = window.innerHeight;
/**/ canvas.id = 'canvas';
/**/ document.body.insertBefore(canvas, document.body.firstChild);
/**/ /* ---- CORE END ---- */
/* ---- CREATING ZONE ---- */
/* ---- SETTINGS ---- */
const numberParticlesStart = 1000;
const particleSpeed = 0.3;
const velocity = 0.9;
const circleWidth = 50;
/* ---- INIT ---- */
let particles = [];
const getRandomFloat = (min, max) => (Math.random() * (max - min) + min);
/* ---- Particle ---- */
function Particle (x, y) {
this.x = x;
this.y = y;
this.vel = {
x : getRandomFloat(-20, 20)/100,
y : getRandomFloat(-20, 20)/100,
min : getRandomFloat(2, 10),
max : getRandomFloat(10, 100)/10
}
this.color = 'rgba(255, 255, 255, 0.05)';
}
Particle.prototype.render = function() {
context.beginPath();
context.fillStyle = this.color;
context.arc(this.x, this.y, 1, 0, Math.PI * 2);
context.fill();
};
Particle.prototype.update = function() {
const forceDirection = {
x: getRandomFloat(-1, 1),
y: getRandomFloat(-1, 1),
};
if (Math.abs(this.vel.x + forceDirection.x) < this.vel.max) {
this.vel.x += forceDirection.x;
}
if (Math.abs(this.vel.y + forceDirection.y) < this.vel.max) {
this.vel.y += forceDirection.y;
}
this.x += this.vel.x * particleSpeed;
this.y += this.vel.y * particleSpeed;
if (Math.abs(this.vel.x) > this.vel.min) {
this.vel.x *= velocity;
}
if (Math.abs(this.vel.y) > this.vel.min) {
this.vel.y *= velocity;
}
this.testBorder();
};
Particle.prototype.testBorder = function() {
if (this.x > windowWidth) {
this.setPosition(this.x, 'x');
} else if (this.x < 0) {
this.setPosition(windowWidth, 'x');
}
if (this.y > windowHeight) {
this.setPosition(this.y, 'y');
} else if (this.y < 0) {
this.setPosition(windowHeight, 'y');
}
};
Particle.prototype.setPosition = function(pos, coor) {
if (coor === 'x') {
this.x = pos;
} else if (coor === 'y') {
this.y = pos;
}
};
/* ---- Functions ----*/
function loop() {
let i;
const length = particles.length;
for (i = 0; i < length; i++) {
particles[i].update();
particles[i].render();
}
requestAnimationFrame(loop);
}
/* ---- START ---- */
function init() {
let i;
for (i = 0; i < numberParticlesStart; i++) {
const angle = Math.random() * 360;
particles.push(new Particle(
windowWidth * 0.5 + (Math.cos(angle) * circleWidth),
windowHeight * 0.5 - (Math.sin(angle) * circleWidth),
));
}
}
init();
window.onresize = () => {
windowWidth = canvas.width = window.innerWidth;
windowHeight = canvas.height = window.innerHeight;
particles = [];
context.clearRect(0,0, windowWidth, windowHeight);
init();
}
window.addEventListener('click', () => {
particles = [];
context.clearRect(0,0, windowWidth, windowHeight);
init();
});
loop();
i replaced the function loop() with this to stop it after 5 seconds -
function loop(stp) {
let i;
const length = particles.length;
for (i = 0; i < length; i++) {
particles[i].update();
particles[i].render();
}
if (stp > 5000) return;
requestAnimationFrame(loop);
}
Try to rely on system clock, as setTimeout and setInterval are not accurate
let start;
function loop(startTime) {
let i;
if(startTime){
// Set the current time in milliseconds from 1st of jenuary 1970
start = new Date().getTime();
}
const length = particles.length;
for (i = 0; i < length; i++) {
particles[i].update();
particles[i].render();
}
// Comparison from current dateTime with the stored one
// as setInterval and setTimeout are not accurate
if (start - (new Date().getTime() >= 5000) return;
requestAnimationFrame(loop);
}
loop(true);

Can't read property 'addEventListener' of null

I am working on a college project that requires me to build a 2D game in javascript. A problem that I'm having at the moment is that it cannot read the 'addEventListener'. This error has caused my game to not work completely.
document.getElementById('restart').addEventListener('click', startGame);
Here is the full code that I have used. The error is down the very bottom.
(function()
{
//Define variables
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var player, score, stop, ticker;
var ground = [], water = [], enemies = [], environment = [];
//Platform variables
var platformHeight, platformLength, gapLength;
var platformWidth = 32;
var platformBase = canvas.height - platformWidth;
var platformSpacer = 64;
//Randomly generates a number
function random(low, high)
{
return Math.floor(Math.random() * (high - low + 1) + low);
}
//Bounds a number
function bound(num, low, high)
{
return Math.max(Math.min(num, high), low);
}
//Loads all of the assets
var assetLoader = (function()
{
this.imgs = {
'bg' : 'Images/bg.png',
'sky' : 'Images/sky.png',
'backdrop' : 'Images/backdrop.png',
'backdrop2' : 'Images/backdrop_ground.png',
'grass' : 'Images/grass.png',
'avatar_normal' : 'Images/normal_walk.png',
'water' : 'imgs/water.png',
'grass1' : 'imgs/grassMid1.png',
'grass2' : 'imgs/grassMid2.png',
'bridge' : 'imgs/bridge.png',
'plant' : 'imgs/plant.png',
'bush1' : 'imgs/bush1.png',
'bush2' : 'imgs/bush2.png',
'cliff' : 'imgs/grassCliffRight.png',
'spikes' : 'imgs/spikes.png',
'box' : 'imgs/boxCoin.png',
'slime' : 'imgs/slime.png'
};
var assetsLoaded = 0; //How many assets have been loaded
var numImgs = Object.keys(this.imgs).length; //Total number of image assets
this.totalAssest = numImgs; //Total number of assets
function assetLoaded(dic, name)
{
if(this[dic][name].status !== 'loading')
{
return;
}
this[dic][name].status = 'loaded';
assetsLoaded++;
if(assetsLoaded === this.totalAssest && typeof this.finished === 'function')
{
this.finished();
}
}
this.downloadAll = function()
{
var _this = this;
var src;
for (var img in this.imgs)
{
if (this.imgs.hasOwnProperty(img))
{
src = this.imgs[img];
(function(_this, img)
{
_this.imgs[img] = new Image();
_this.imgs[img].status = 'loading';
_this.imgs[img].name = img;
_this.imgs[img].onload = function() {assetLoaded.call(_this, 'imgs', img)};
_this.imgs[img].src = src;
})(_this, img);
}
}
}
return{
imgs: this.imgs,
totalAssest: this.totalAssest,
downloadAll: this.downloadAll
};
})();
assetLoader.finished = function()
{
startGame();
}
function SpriteSheet(path, frameWidth, frameHeight)
{
this.image = new Image();
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
var self = this;
this.image.onload = function()
{
self.framesPerRow = Math.floor(self.image.width / self.frameWidth);
};
this.image.src = path;
}
function Animation(spritesheet, frameSpeed, startFrame, endFrame)
{
var animationSequence = [];
var currentFrame = 0;
var counter = 0;
for (var frameNumber = startFrame; frameNumber <= endFrame; frameNumber++)
{
animationSequence.push(frameNumber);
}
this.update = function()
{
if (counter == (frameSpeed - 1))
{
currentFrame = (currentFrame + 1) % animationSequence.length;
}
counter = (counter + 1) % frameSpeed;
};
this.draw = function(x, y)
{
var row = Math.floor(animationSequence[currentFrame] / spritesheet.framesPerRow);
var col = Math.floor(animationSequence[currentFrame] % spritesheet.framesPerRow);
ctx.drawImage
(
spritesheet.image,
col * spritesheet.frameWidth, row * spritesheet.frameHeight,
spritesheet.frameWidth, spritesheet.frameHeight,
x, y,
spritesheet.frameWidth, spritesheet.frameHeight);
};
}
var background = (function()
{
var sky = {};
var backdrop = {};
var backdrop2 = {};
this.draw = function()
{
ctx.drawImage(assetLoader.imgs.bg, 0, 0);
sky.x -= sky.speed;
backdrop.x -= backdrop.speed;
backdrop2.x -= backdrop2.speed;
ctx.drawImage(assetLoader.imgs.sky, sky.x, sky.y);
ctx.drawImage(assetLoader.imgs.sky, sky.x + canvas.width, sky.y);
ctx.drawImage(assetLoader.imgs.backdrop, backdrop.x, backdrop.y);
ctx.drawImage(assetLoader.imgs.backdrop, backdrop.x + canvas.width, backdrop.y);
ctx.drawImage(assetLoader.imgs.backdrop2, backdrop2.x, backdrop2.y);
ctx.drawImage(assetLoader.imgs.backdrop2, backdrop2.x + canvas.width, backdrop2.y);
if (sky.x + assetLoader.imgs.sky.width <= 0)
{
sky.x = 0;
}
if (backdrop.x + assetLoader.imgs.backdrop.width <= 0)
{
backdrop.x = 0;
}
if (backdrop2.x + assetLoader.imgs.backdrop2.width <= 0)
{
backdrop2.x = 0;
}
};
this.reset = function()
{
sky.x = 0;
sky.y = 0;
sky.speed = 0.2;
backdrop.x = 0;
backdrop.y = 0;
backdrop.speed = 0.4;
backdrop2.x = 0;
backdrop2.y = 0;
backdrop2.speed = 0.6;
}
return{
draw: this.draw,
reset: this.reset
};
})();
//A vector for 2D space
function Vector(x, y, dx, dy)
{
// position
this.x = x || 0;
this.y = y || 0;
// direction
this.dx = dx || 0;
this.dy = dy || 0;
}
//Advances the vector's position
Vector.prototype.advance = function()
{
this.x += this.dx;
this.y += this.dy;
};
//Gets the minimum distance between two vectors
Vector.prototype.minDist = function(vec)
{
var minDist = Infinity;
var max = Math.max(Math.abs(this.dx), Math.abs(this.dy),Math.abs(vec.dx), Math.abs(vec.dy));
var slice = 1 / max;
var x, y, distSquared;
// get the middle of each vector
var vec1 = {}, vec2 = {};
vec1.x = this.x + this.width/2;
vec1.y = this.y + this.height/2;
vec2.x = vec.x + vec.width/2;
vec2.y = vec.y + vec.height/2;
for(var percent = 0; percent < 1; percent += slice)
{
x = (vec1.x + this.dx * percent) - (vec2.x + vec.dx * percent);
y = (vec1.y + this.dy * percent) - (vec2.y + vec.dy * percent);
distSquared = x * x + y * y;
minDist = Math.min(minDist, distSquared);
}
return Math.sqrt(minDist);
};
//The player object
var player = (function(player)
{
//Player properties
player.width = 60;
player.height = 96;
player.speed = 6;
//Jumping
player.gravity = 1;
player.dy = 0;
player.jumpDy = -10;
player.isFalling = false;
player.isJumping = false;
//Spritesheets
player.sheet = new SpriteSheet('Images/normal_walk.png', player.width, player.height);
player.walkAnim = new Animation(player.sheet, 4, 0, 15);
player.jumpAnim = new Animation(player.sheet, 4, 15, 15);
player.fallAnim = new Animation(player.sheet, 4, 11, 11);
player.anim = player.walkAnim;
Vector.call(player, 0, 0, 0, player.dy);
var jumpCounter = 0;
player.update = function()
{
//Jump if not currently jumping or falling
if(KEY_STATUS.space && player.dy === 0 && !player.isJumping)
{
player.isJumping = true;
player.dy = player.jumpDy;
jumpCounter = 12;
}
//Jump higher if the spacebar is continually pressed
if(KEY_STATUS.space && jumpCounter)
{
player.dy = player.jumpDy;
}
jumpCounter = Math.max(jumpCounter - 1, 0);
this.advance();
//Gravity
if(player.isFalling || player.isJumping)
{
player.dy += player.gravity;
}
//Falling Animation
if(player.dy > 0)
{
player.anim = player.fallAnim;
}
// change animation is jumping
else if(player.dy < 0)
{
player.anim = player.jumpAnim;
}
else
{
player.anim = player.walkAnim;
}
player.anim.update();
};
//Draw the player's current position
player.draw = function()
{
player.anim.draw(player.x, player.y);
};
//Resets the player's position
player.reset = function()
{
player.x = 64;
player.y = 250;
};
return player;
})(Object.create(Vector.prototype));
//Sprites
function Sprite(x, y, type)
{
this.x = x;
this.y = y;
this.width = platformWidth;
this.height = platformWidth;
this.type = type;
Vector.call(this, x, y, 0, 0);
//Updating the sprites
this.update = function()
{
this.dx = -player.speed;
this.advancer();
}
//Drawing the sprites
this.draw = function()
{
ctx.save();
ctx.translate(0.5, 0.5);
ctx.drawImage(assetLoader.imgs[this.type], this.x, this.y);
ctx.restore();
}
}
Sprite.prototype = Object.create(Vector.prototype);
//Platforms
function getType()
{
var type;
switch(platformHeight)
{
case 0:
case 1:
type = Math.random() > 0.5 ? 'grass1' : 'grass2';
break;
case 2:
type = 'grass';
break;
case 3:
type = 'bridge';
break;
case 4:
type = 'box';
break;
}
if (platformLength === 1 && platformHeight < 3 && rand(0, 3) === 0)
{
type = 'cliff';
}
return type;
}
//Update and draw all ground positions
function updateGround()
{
//Animate ground
player.isFalling = true;
for(var i = 0; i < ground.length; i++)
{
ground[i].update();
ground[i].draw();
//Stop the player going through the platforms when landing
var angle;
if(player.minDist(ground[i]) <= player.height/2 + platformWidth/2 && (angle = Math.atan2(player.y - ground[i].y, player.x - ground[i].x) * 180/Math.PI) > -130 &&angle < -50)
{
player.isJumping = false;
player.isFalling = false;
player.y = ground[i].y - player.height + 5;
player.dy = 0;
}
}
//Remove the ground that has gone off screen
if(ground[0] && ground[0].x < -platformWidth)
{
ground.splice(0, 1);
}
}
//Update and draw all water positions
function updateWater()
{
//Animate water
for(var i = 0; i < water.length; i++)
{
water[i].update();
water[i].draw();
}
//Remove water that has gone off screen
if (water[0] && water[0].x < -platformWidth)
{
var w = water.splice(0, 1)[0];
w.x = water[water.length-1].x + platformWidth;
water.push(w);
}
}
//Update and draw all environment positions
function updateEnvironment()
{
//Animate environment
for(var i = 0; i < environment.length; i++)
{
environment[i].update();
environment[i].draw();
}
//R emove environment that have gone off screen
if(environment[0] && environment[0].x < -platformWidth)
{
environment.splice(0, 1);
}
}
//Update and draw all enemies position. Also check for collision against the player.
function updateEnemies()
{
//Animate enemies
for(var i = 0; i < enemies.length; i++)
{
enemies[i].update();
enemies[i].draw();
//Player ran into enemy
if(player.minDist(enemies[i]) <= player.width - platformWidth/2)
{
gameOver();
}
}
//Remove enemies that have gone off screen
if(enemies[0] && enemies[0].x < -platformWidth)
{
enemies.splice(0, 1);
}
}
//Update and draw the players position
function updatePlayer()
{
player.update();
player.draw();
//Game over
if(player.y + player.height >= canvas.height)
{
gameOver();
}
}
//Spawn new sprites off screen
function spawnSprites()
{
//Increase score
score++;
//First create a gap
if(gapLength > 0)
{
gapLength--;
}
//Then create the ground
else if(platformLength > 0)
{
var type = getType();
ground.push(new Sprite(
canvas.width + platformWidth % player.speed,
platformBase - platformHeight * platformSpacer,
type
));
platformLength--;
//Add random environment sprites
spawnEnvironmentSprites();
//Add random enemies
spawnEnemySprites();
}
//Start over
else
{
//Increase gap length every speed increase of 4
gapLength = rand(player.speed - 2, player.speed);
// only allow a ground to increase by 1
platformHeight = bound(rand(0, platformHeight + rand(0, 2)), 0, 4);
platformLength = rand(Math.floor(player.speed/2), player.speed * 4);
}
}
//Spawn new environment sprites off screen
function spawnEnvironmentSprites()
{
if(score > 40 && rand(0, 20) === 0 && platformHeight < 3)
{
if (Math.random() > 0.5)
{
environment.push(new Sprite(canvas.width + platformWidth % player.speed, platformBase - platformHeight * platformSpacer - platformWidth, 'plant'));
}
else if(platformLength > 2)
{
environment.push(new Sprite(canvas.width + platformWidth % player.speed, platformBase - platformHeight * platformSpacer - platformWidth, 'bush1'));
environment.push(new Sprite(canvas.width + platformWidth % player.speed + platformWidth, platformBase - platformHeight * platformSpacer - platformWidth, 'bush2'));
}
}
}
//Spawn new enemy sprites off screen
function spawnEnemySprites()
{
if(score > 100 && Math.random() > 0.96 && enemies.length < 3 && platformLength > 5 && (enemies.length ? canvas.width - enemies[enemies.length-1].x >= platformWidth * 3 || canvas.width - enemies[enemies.length-1].x < platformWidth : true))
{
enemies.push(new Sprite(canvas.width + platformWidth % player.speed, platformBase - platformHeight * platformSpacer - platformWidth, Math.random() > 0.5 ? 'spikes' : 'slime'));
}
}
//Game Loop
function animate()
{
if(!stop)
{
requestAnimFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height);
background.draw();
//Update entities
updateWater();
updateEnvironment();
updatePlayer();
updateGround();
updateEnemies();
//Draw the score
ctx.fillText('Score: ' + score + 'm', canvas.width - 140, 30);
//Spawn a new Sprite
if(ticker % Math.floor(platformWidth / player.speed) === 0)
{
spawnSprites();
}
//Increase player's speed only when player is jumping
if(ticker > (Math.floor(platformWidth / player.speed) * player.speed * 20) && player.dy !== 0)
{
player.speed = bound(++player.speed, 0, 15);
player.walkAnim.frameSpeed = Math.floor(platformWidth / player.speed) - 1;
//Reset ticker
ticker = 0;
//Spawn a platform to fill in gap created by increasing player speed
if(gapLength === 0)
{
var type = getType();
ground.push(new Sprite(canvas.width + platformWidth % player.speed, platformBase - platformHeight * platformSpacer, type));
platformLength--;
}
}
ticker++;
}
}
//Spacebar events
var KEY_CODES = {
32: 'space'
};
var KEY_STATUS = {};
for(var code in KEY_CODES)
{
if(KEY_CODES.hasOwnProperty(code))
{
KEY_STATUS[KEY_CODES[code]] = false;
}
}
document.onkeydown - function(e)
{
var keyCode = (e.keyCode) ? e.keyCode : e.charCode;
if(KEY_CODES[keyCode])
{
e.preventDefault();
KEY_STATUS[KEY_CODES[keyCode]] = true;
}
};
document.onkeydown - function(e)
{
var keyCode = (e.keyCode) ? e.keyCode : e.charCode;
if(KEY_CODES[keyCode])
{
e.preventDefault();
KEY_STATUS[KEY_CODES[keyCode]] = false;
}
};
//Request Animation Polyfill
var requestAnimFrame = (function()
{
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback, element)
{
window.setTimeout(callback, 1000 / 60);
};
})();
//Start the game and resets all variables and entities, spawn ground and water.
function startGame()
{
document.getElementById('game-over').style.display = 'none';
ground = [];
water = [];
environment = [];
enemies = [];
player.reset();
ticker = 0;
stop = false;
score = 0;
platformHeight = 2;
platformLength = 15;
gapLength = 0;
ctx.font = '16px arial, sans-serif';
for (var i = 0; i < 30; i++)
{
ground.push(new Sprite(i * (platformWidth-3), platformBase - platformHeight * platformSpacer, 'grass'));
}
for (i = 0; i < canvas.width / 32 + 2; i++)
{
water.push(new Sprite(i * platformWidth, platformBase, 'water'));
}
background.reset();
animate();
}
//End the game and restart
function gameOver()
{
stop = true;
document.getElementById('game-over').style.display = 'block';
}
document.getElementById('restart').addEventListener('click', startGame);
assetLoader.downloadAll();
})();

Categories