Stopping animated rain in Canvas smmoothly - javascript

Rain Picture HereSo my issue isn't stopping the rain its stopping the making of the rain so the already rendered rain completes its animation off the screen.
I tried setInterval and setTimeout in JS but it just freezes.
And using JQuery to remove canvas tag takes off all drops at once.
Any ideas or direction would be great!
var canvas = document.getElementById("rainCanvas");
var ctx = canvas.getContext("2d");
var canvasW = window.innerWidth;
var canvasH = window.innerHeight;
canvas.height = canvasH;
canvas.width = canvasW;
var mf = 70;
var drops = [];
for(var i = 0; i < mf; i++){
drops.push({
x: Math.random()*canvasW,
y: Math.random()*canvasH,
r: Math.random()*5+2,
d: Math.random() + 1
})
}
function fill() {
ctx.fill();
}
function drawRain(){
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.fillStyle = "rgba(255, 255, 255, .5)";
ctx.beginPath();
for(var i = 0; i < mf; i++){
var f = drops[i];
ctx.moveTo(f.x-5, f.y);
ctx.lineTo(f.x, f.y-15);
ctx.lineTo(f.x+5, f.y);
ctx.arc(f.x, f.y + f.r*.7,5, 0, Math.PI, false);
}
fill();
moveRain();
}
function moveRain(){
for(var i = 0; i < mf; i++){
var f = drops[i];
f.y += Math.pow(f.d, 2) + 1;
if(f.y > canvasH){
drops[i] = {x: Math.random()*canvasW, y: 0, r: f.r, d: f.d};
}
}
}
var i = setInterval(drawRain, 20);
setTimeout(function( ) { clearInterval(); }, 2000);
canvas{ background-color: black }
<canvas id="rainCanvas"></canvas>

What you want to do is set a flag to stop the rain from wrapping back to the top in your function to stop raining:
var stopRain = false;
...
setTimeout(function( ) { stopRain = true; }, 2000);
Now inside moveRain when that variable is true instead of moving it back to the top remove it from the array. Once we removed all the drops we can then clear the interval as it's no longer needed:
function moveRain(){
for(var i = 0; i < mf; i++){
var f = drops[i];
f.y += Math.pow(f.d, 2) + 1;
if(f.y > canvasH){
if(stopRain) { // When we stop raining
drops.splice(i, 1); // Remove drop from array
mf--; // Make sure to update the "length"
if(mf<1) clearInterval(i); // If there are not more drops clear the interval
} else drops[i] = {x: Math.random()*canvasW, y: 0, r: f.r, d: f.d};
}
}
}
Fiddle Example
You could also use drops.length instead of using mf, this way you don't need to do mf--.

Here I fixed some of the code and added a rainDensity variable which controls the count of new raindrops. The original code was designed to have a fixed amount of raindrops, it had to be changed to achieve the desired effect.
window.onload = function(){
var canvas = document.getElementById("rainCanvas");
var ctx = canvas.getContext("2d");
var canvasW = window.innerWidth;
var canvasH = window.innerHeight;
canvas.height = canvasH;
canvas.width = canvasW;
var drops = [];
function makeDrop() {
drops.push({
x: Math.random()*canvasW,
y: -10,
r: Math.random()*4+1,
d: Math.pow(Math.random() + 1, 2) + 1
})
}
function drawRain(){
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.fillStyle = "rgba(128, 128, 255, .5)";
for(var f of drops){
ctx.beginPath();
ctx.moveTo(f.x-5, f.y);
ctx.lineTo(f.x, f.y-15);
ctx.lineTo(f.x+5, f.y);
ctx.arc(f.x, f.y + f.r*.7,5, 0, Math.PI, false);
ctx.fill();
}
}
function handleFrame() {
drawRain();
updateRain();
}
function updateRain() {
moveRain();
makeNewDrops();
}
var dropPerFrameCounter = 0;
var startTime = Date.now();
function makeNewDrops() {
var elapsedTime = (Date.now() - startTime) / 1000;
// rainDensity: set it to 0 to stop rain
var rainDensity = Math.max(0, Math.sin(elapsedTime / 3) + 0.5);
dropPerFrameCounter += rainDensity;
while (dropPerFrameCounter >= 1) {
dropPerFrameCounter--;
makeDrop();
}
}
function moveRain() {
for(var f of drops){
f.y += f.d;
}
drops = drops.filter(d => d.y < canvasH);
}
var intervalRender = setInterval(handleFrame, 20);
}
<canvas id="rainCanvas"></canvas>

Had to add an answer because the example all used setInterval to do the animation which looks awful. Use requestAnimationFrame to animate. Also fixed some other problems with the code.
I have added a pause the pauses the animation when you click on the canvas. Clicking again will continue.
var canvas = document.getElementById("rainCanvas");
var ctx = canvas.getContext("2d");
var canvasW = window.innerWidth;
var canvasH = window.innerHeight;
canvas.height = canvasH;
canvas.width = canvasW;
var mf = 70;
var drops = [];
var paused = false;
for(var i = 0; i < mf; i++){
drops.push({
x: Math.random()*canvasW,
y: Math.random()*canvasH,
r: Math.random()*5+2,
d: Math.random() + 1
})
}
function drawRain(){
if(!paused){
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.fillStyle = "rgba(255, 255, 255, .5)";
ctx.beginPath();
for(var i = 0; i < mf; i++){
var f = drops[i];
ctx.moveTo(f.x - 2.5 * f.d, f.y);
ctx.lineTo(f.x, f.y - 7.5 * f.d);
ctx.lineTo(f.x + 2.5 * f.d, f.y);
ctx.arc(f.x, f.y + f.r * 0.7, 2.5 * f.d, 0, Math.PI, false);
}
ctx.fill();
for(var i = 0; i < mf; i++){
var f = drops[i];
f.y += Math.pow(f.d, 2) + 1;
if(f.y > canvasH + 15){ // make sure completely off the bottom
// dont create a new drop reuse it will stop GC from
// having to interrupt the code.
f.x = Math.random()*canvasW;
f.y = -6; // move off top
}
}
}
requestAnimationFrame(drawRain);
}
requestAnimationFrame(drawRain);
canvas.addEventListener("click",()=>{paused = ! paused});
canvas{
background-color: black;
position : absolute;
top : 0px;
left : 0px;
}
<canvas id="rainCanvas"></canvas>

Related

Initializing a possibly infinite array?

I'm attempting to make sand fall from the mouse (i've changed the x and y of the sand to be the center of the canvas for testing purposes.)
I'm having some issues with one of my functions. drawSand() will not run - I believe it's because sandObj[] can't be initialized before hand, but i'm uncertain. i've tried initializing it by just limiting the array to 200 elements but that didn't seem to help any. I've added some booleans to test to see if the function is finishing and draw is the only one that isn't (besides the drawPaintTool, it's not enabled on purpose) Any help / criticism would be helpful!
"use strict";
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var ballX = 0;
var ballY = 0;
var ballRadius = 20;
var sandObj = [];
var sandActive = false;
var sandAmount = 10;
var sandX = 10;
var sandY = 0;
var testDrawPaintToolFunction = false;
var testPaintToolFunction = false;
var testDrawSandFunction = false;
var testMoveSandFunction = false;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
sandAmountDisplay();
paintTool();
moveSand();
drawSand();
}
function drawPaintTool() {
ctx.beginPath();
ctx.arc(ballX, ballY, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "white";
ctx.fill();
ctx.closePath();
testDrawPaintToolFunction = true;
}
function paintTool() {
if (sandActive == true) {
sandAmount++;
}
testPaintToolFunction = true;
}
function drawSand() {
for (var i = 0; i < sandAmount; i++) {
sandObj = {
x: canvas.width / 2,
y: canvas.height / 2
};
ctx.beginPath();
ctx.rect(sandObj[i].x, sandObj[i].y, 5, 5);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
testDrawSandFunction = true;
}
function moveSand() {
testMoveSandFunction = true;
}
function sandAmountDisplay() {
ctx.font = "16px Arial";
ctx.fillStyle = "black";
ctx.fillText("Sand Amount: " + sandAmount, 8, 20);
ctx.fillText("Sand Active: " + sandActive, 8, 40);
ctx.fillText("drawPaintTool: " + testDrawPaintToolFunction, 8, 60);
ctx.fillText("PaintTool: " + testPaintToolFunction, 8, 80);
ctx.fillText("drawSand: " + testDrawSandFunction, 8, 100);
ctx.fillText("moveSand: " + testMoveSandFunction, 8, 120);
}
document.addEventListener("mousemove", mouseMoveHandler, false);
document.addEventListener("mousedown", mouseDownHandler);
document.addEventListener("mouseup", mouseUpHandler);
function mouseMoveHandler(e) {
var relativeX = e.clientX - canvas.offsetLeft + ballRadius / 2;
var relativeY = e.clientY - canvas.offsetTop + ballRadius / 2;
if (relativeX > 0 && relativeX < canvas.width) {
ballX = relativeX - ballRadius / 2;
}
if (relativeY > 0 && relativeY < canvas.height) {
ballY = relativeY - ballRadius / 2;
}
}
function mouseDownHandler() {
sandActive = true;
}
function mouseUpHandler() {
sandActive = false;
}
setInterval(draw, 10);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Gamedev Canvas Workshop</title>
<style>
* {
padding: 0;
margin: 15;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="480" height="320"></canvas>
<script src="sand.js">
</script>
</body>
</html>
so to just help out some, because if I comment the code I would most likely have to write a larger paragraph just to get this posted, i'm going to explain the drawSand function. So the for loop used is for re-drawing all the sand everytime it's called - however, i've had to set the x and y here because I couldn't think of a way to initialize something that could continuously spawn sand. I'm lost to be honest.
EDIT: Also the sandAmount is changing constantly which is most likely the problem with something like this. when the mouse is held down the sandAmount goes up - which is what paintTool is for.
Inside of this function:
function drawSand() {
for (var i = 0; i < sandAmount; i++) {
sandObj = {
x: canvas.width / 2,
y: canvas.height / 2
};
ctx.beginPath();
ctx.rect(sandObj[i].x, sandObj[i].y, 5, 5);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
testDrawSandFunction = true;
}
...you are assigning a new object value to sandObj, which was previously an array. You are accessing it as an array with sandObj[i]... further down, so I'm guessing that was a mistake on your part. Perhaps you meant to do:
sandObj[i] = {
x: canvas.width / 2,
y: canvas.height / 2
};
...rather than:
sandObj = {
x: canvas.width / 2,
y: canvas.height / 2
};

Create bullets for a game at an interval

I have a game, and in it there is a main, controllable character and then enemies that shoot back at the character. For the enemies, when they shoot back I want them to shoot at intervals so that it isn't just one massive block of bullets, and it worked with a setInterval for one, but when a second enemy comes in they don't shoot. Only one of the two will. If anybody has a solution that would be great!
function enemies() {
if (enemy_soldiers.length == 0) {
level += 0.2;
for (var i = 0; i<(1 + Math.floor(Math.round(level))); i++) {
var gx = 1450
var gy = getRandom(430, 630);
enemy_soldiers.push({
x: gx,
y: gy,
l: gl,
d: getRandom(350, 600),
shooting: false,
interval: setInterval (function() {enemy.shooting = true;},fire_rate),
shoot: function() {
enemy_bullets.push({
x: enemy.x+40,
y: enemy.y+87,
vel: 10,
});
}
});
}
}
var enemy;
gctx.clearRect(0, 0, 1400, 800);
for (var i in enemy_soldiers) {
enemy = enemy_soldiers[i];
drawenemy(enemy.x, enemy.y, enemy.l);
//ai
if (distance(enemy.x, enemy.y, cx, cy) >= enemy.d && enemy.x>cx) {
enemy.x-=vel;
}
else if (distance(enemy.x, enemy.y, cx, cy) >= enemy.d && enemy.x<cx) {
enemy.x+=vel;
}
if (distance(enemy.x, enemy.y, cx, cy) <= 600) {
if (enemy.shooting == true) {
enemy.shoot(enemy.x,enemy.y);
enemy.shooting = false;
}
gbctx.clearRect(0, 0, 1400, 800);
for (var j in enemy_bullets) {
enemy_bullet = enemy_bullets[j];
enemy_bullet.x -= enemy_bullet.vel;
if (enemy_bullet.x > 1400 || enemy_bullet.x < -5 || enemy_bullet.y > 800 || enemy_bullet.y < -5) {
enemy_bullets.splice(j,1);
}
drawEnemyBullet(enemy_bullet.x, enemy_bullet.y);
}
}
}}
This solution relies on the function Date.now() which returns the current time in milliseconds. It's possible for each enemy to keep track of when they have to fire next and each one can have a different delay between shots.
I've only added a graphic to show when they are firing their weapons, but this can be easily altered to something like a raycast or spawn a projectile particle.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
position: absolute;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
border: solid 1px white;
border-radius: 1px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
// Enemy constructor
function Enemy(x,y) {
this.x = x;
this.y = y;
this.isFiring = false;
this.fireDelay = (500 + Math.random() * 500) | 0; // Time between shots
this.lastFired = Date.now(); // Last time at which the enemy fired
}
/*
Enemy prototype
(All objects created using the constructor share these properties)
E.G.
var e1 = new Enemy(10,0);
var e2 = new Enemy(20,0);
if (e1.__proto__ === e2.__proto__) {
console.log("Match");
}
prints "Match"
*/
Enemy.prototype = {
WIDTH: 10,
HEIGHT: 20,
FIRE_DURATION: 100, // Amount of time 'fire' graphic is shown
FIRE_WIDTH: 10,
FIRE_HEIGHT: 10,
update: function() {
// If current time - time when I last fired > the amount of time between shots
if (Date.now() - this.lastFired > this.fireDelay) {
this.lastFired = Date.now();
this.isFiring = true;
// If you were using projectile particles, this is where you would spawn one
}
if (this.isFiring && Date.now() - this.lastFired > this.FIRE_DURATION) {
this.isFiring = false;
}
},
render: function(ctx) {
ctx.fillStyle = "darkred";
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.rect(
this.x,
this.y,
this.WIDTH,
this.HEIGHT
);
ctx.fill();
ctx.stroke();
if (this.isFiring) {
ctx.fillStyle = "yellow";
ctx.strokeStyle = "darkyellow";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.rect(
this.x + this.WIDTH,
this.y + this.HEIGHT * 0.5 - this.FIRE_HEIGHT * 0.5,
this.FIRE_WIDTH,
this.FIRE_HEIGHT
);
ctx.fill();
ctx.stroke();
}
}
};
var canvasWidth = 180;
var canvasHeight = 160;
var canvas = null;
var ctx = null;
var enemies = [];
window.onload = function() {
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
ctx = canvas.getContext("2d");
for (var i = 0; i < 5; ++i) {
enemies[i] = new Enemy(20 + i * 3,10 + i * 30);
}
loop();
}
function loop() {
// Update
for (var i = 0; i < enemies.length; ++i) {
enemies[i].update();
}
// Render
ctx.fillStyle = "gray";
ctx.fillRect(0,0,canvasWidth,canvasHeight);
for (var i = 0; i < enemies.length; ++i) {
enemies[i].render(ctx);
}
//
requestAnimationFrame(loop);
}
</script>
</body>
</html>

Use same animation for multiple elements

I have this working canvas javascript animation but i would like to use it multiple times, currently it's only possible to have one canvas element with the id "stars" and use that one. Could i perhaps add a class for them instead and get the elements class and loop or what would be my best solution for achieving this? I would like to make this work without repeating to much since i could end up using the animation on different pages.
// Settings
var particleCount = 40,
flareCount = 0,
motion = 0.05,
tilt = 0.05,
color = '#00FF7B',
particleSizeBase = 1,
particleSizeMultiplier = 0.5,
flareSizeBase = 100,
flareSizeMultiplier = 100,
lineWidth = 1,
linkChance = 75, // chance per frame of link, higher = smaller chance
linkLengthMin = 5, // min linked vertices
linkLengthMax = 7, // max linked vertices
linkOpacity = 0.25; // number between 0 & 1
linkFade = 90, // link fade-out frames
linkSpeed = 0, // distance a link travels in 1 frame
glareAngle = -60,
glareOpacityMultiplier = 0.4,
renderParticles = true,
renderParticleGlare = true,
renderFlares = false,
renderLinks = false,
renderMesh = false,
flicker = false,
flickerSmoothing = 15, // higher = smoother flicker
blurSize = 0,
orbitTilt = true,
randomMotion = true,
noiseLength = 1000,
noiseStrength = 3;
var canvas = document.getElementById('stars'),
context = canvas.getContext('2d'),
mouse = {
x: 0,
y: 0
},
m = {},
r = 0,
c = 1000, // multiplier for delaunay points, since floats too small can mess up the algorithm
n = 0,
nAngle = (Math.PI * 2) / noiseLength,
nRad = 100,
nScale = 0.5,
nPos = {
x: 0,
y: 0
},
points = [],
vertices = [],
triangles = [],
links = [],
particles = [],
flares = [];
function init() {
var i, j, k;
// requestAnimFrame polyfill
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
// Size canvas
resize();
mouse.x = canvas.clientWidth / 2;
mouse.y = canvas.clientHeight / 2;
// Create particle positions
for (i = 0; i < particleCount; i++) {
var p = new Particle();
particles.push(p);
points.push([p.x * c, p.y * c]);
}
vertices = Delaunay.triangulate(points);
var tri = [];
for (i = 0; i < vertices.length; i++) {
if (tri.length == 3) {
triangles.push(tri);
tri = [];
}
tri.push(vertices[i]);
}
// Tell all the particles who their neighbors are
for (i = 0; i < particles.length; i++) {
// Loop through all tirangles
for (j = 0; j < triangles.length; j++) {
// Check if this particle's index is in this triangle
k = triangles[j].indexOf(i);
// If it is, add its neighbors to the particles contacts list
if (k !== -1) {
triangles[j].forEach(function(value, index, array) {
if (value !== i && particles[i].neighbors.indexOf(value) == -1) {
particles[i].neighbors.push(value);
}
});
}
}
}
var fps = 15;
var now;
var then = Date.now();
var interval = 1000 / fps;
var delta;
// Animation loop
(function animloop() {
requestAnimFrame(animloop);
now = Date.now();
delta = now - then;
if (delta > interval) {
then = now - (delta % interval);
resize();
render();
}
})();
}
function render() {
if (randomMotion) {
n++;
if (n >= noiseLength) {
n = 0;
}
nPos = noisePoint(n);
}
if (renderParticles) {
// Render particles
for (var i = 0; i < particleCount; i++) {
particles[i].render();
}
}
}
function resize() {
canvas.width = window.innerWidth * (window.devicePixelRatio || 1);
canvas.height = canvas.width * (canvas.clientHeight / canvas.clientWidth);
}
// Particle class
var Particle = function() {
this.x = random(-0.1, 1.1, true);
this.y = random(-0.1, 1.1, true);
this.z = random(0, 4);
this.color = color;
this.opacity = random(0.1, 1, true);
this.flicker = 0;
this.neighbors = []; // placeholder for neighbors
};
Particle.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * particleSizeMultiplier) + particleSizeBase) * (sizeRatio() / 1000),
o = this.opacity;
context.fillStyle = this.color;
context.globalAlpha = o;
context.beginPath();
context.fill();
context.closePath();
if (renderParticleGlare) {
context.globalAlpha = o * glareOpacityMultiplier;
context.ellipse(pos.x, pos.y, r * 100, r, (glareAngle - ((nPos.x - 0.5) * noiseStrength * motion)) * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
}
context.globalAlpha = 1;
};
// Flare class
// Link class
var Link = function(startVertex, numPoints) {
this.length = numPoints;
this.verts = [startVertex];
this.stage = 0;
this.linked = [startVertex];
this.distances = [];
this.traveled = 0;
this.fade = 0;
this.finished = false;
};
// Utils
function noisePoint(i) {
var a = nAngle * i,
cosA = Math.cos(a),
sinA = Math.sin(a),
rad = nRad;
return {
x: rad * cosA,
y: rad * sinA
};
}
function position(x, y, z) {
return {
x: (x * canvas.width) + ((((canvas.width / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * z) * motion),
y: (y * canvas.height) + ((((canvas.height / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * z) * motion)
};
}
function sizeRatio() {
return canvas.width >= canvas.height ? canvas.width : canvas.height;
}
function random(min, max, float) {
return float ?
Math.random() * (max - min) + min :
Math.floor(Math.random() * (max - min + 1)) + min;
}
// init
if (canvas) init();
html,
body {
margin: 0;
padding: 0;
height: 100%;
}
body {
background: #000;
background-image: linear-gradient(-180deg, rgba(0, 0, 0, 0.00) 0%, #000000 100%);
}
#stars {
display: block;
position: relative;
width: 100%;
height: 100%;
z-index: 1;
position: absolute;
}
<script src="https://rawgit.com/ironwallaby/delaunay/master/delaunay.js"></script>
<script src="http://requirejs.org/docs/release/2.1.15/minified/require.js"></script>
<canvas id="stars" width="300" height="300"></canvas>
id="identifier" is unique
class="identifier" could be shared by a list of items
As you mention using class could be an option but you'll need to change your code to select all elements by that class before:
$(".identifier").each(function(a,b)
{
// Actions for each element
}
With javascript:
var elementList = document.getElementsByClassName("identifier");
var elementListSize=elementList.length;
for(var i=0;i<elementListSize;i++) {
// Actions for each element (elementList[i])
}

JavaScript canvas - I want to remove the scrollbar

JavaScript canvas - I want to remove the x-axis scrollbar.
Is it to do with innerWidth? Or is it something to do with my device/browser - I'm on a MacBook Air and using google chrome.
var canvas = document.getElementById('sky');
var ctx = canvas.getContext('2d')
var w = window.innerWidth;
var h = window.innerHeight;
canvas.width = w;
canvas.height = h;
// GENERATE THE stars
var mf = 10000; //QUANTITY OF stars
var flakes = [];
//loop through empty stars and apply attributes('width,height')
for(var i = 0; i < mf; i++)
{
flakes.push({
x: Math.random() * w,
y: Math.random() * h,
r: Math.random() * 2, // radius of each star = min 2px max 7px
d: Math.random() + 1// like the weight, how far they would fall
})
}
//draw flakes on the canvas
function drawFlakes()
{
ctx.clearRect(0, 0, w, h); // clears canvas
ctx.fillStyle = "#ffff00"; // fill the canvas or shapes will be white
ctx.beginPath(); // about to begin a path or draw shape
for(var i = 0; i < mf; i++) // going through each star
{
var f = flakes[i]; // grabbing fstar
ctx.moveTo(f.x, f.y);
ctx.arc(f.x, f.y, f.r, 0, Math.PI*1, true);
}
ctx.fill(); // fills flakes
moveFlakes();
}
// animate flakes
var angle = 0; // controls movement of flakes
function moveFlakes(){
angle += 0.01;
for(var i = 0; i < mf; i++)
{
//store current flake
var f = flakes[i];
//update x any coorodinates of each flake
f.y += Math.pow(f.d, 2) + 1;
f.x += Math.sin(angle) * 60;
//if snows flakes reach bottom, send a new one to the top
if(f.y > h) {
flakes[i] = {
x: Math.random()* w,
y: 0,
r: f.r,
d: f.d
}
}
}
}
setInterval(drawFlakes, 25);
}
Try adding this style to body tag overflow: hidden

Why is this clearInterval not reaching the var, though it is set as global?

JSFiddle
After each circle object is created, it should increase in size while the mouse is down, and stop when mouse is up. clearInterval doesn't seem to reach the internal variable "growLoop" even though it's supposed to be global by declaring it first(which was the advice on many other posts about this same issue). In the console it shows growLoop undefined, but it's defined in line 95, right?
Also, the time interval seems to decrease with every new circle created, and they grow faster. How could the value of setInterval change?
//set up canvas
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var circles = [];
//create circle
function create(location) {
circles.push({
x: location.x,
y: location.y,
radius: 10,
color: '#' + Math.floor(Math.random() * 16777215).toString(16)
});
}
//figure out mouse position
var rect = document.getElementById("canvas").getBoundingClientRect();
// Get canvas offset on page
var offset = {
x: rect.left,
y: rect.top
};
function isOnCanvas(a) {
if ((a.x >= 0 && a.x <= rect.width) && (a.y >= 0 && a.y <= rect.height)) {
return true;
}
return false;
}
function isOnCircle(a) {
var i = 0,
l = circles.length,
x, y, d, c;
for (; i < l; ++i) {
c = circles[i];
x = a.x - c.x;
y = a.y - c.y;
d = (a.radius || 10) + c.radius;
if (x * x + y * y <= d * d) {
return true;
}
}
return false;
}
// draw all circles
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < circles.length; i++) {
var p = circles[i];
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI);
ctx.fillStyle = p.color;
ctx.fill();
}
}
//make last drawn circle 1px bigger
function grow(){
var a = circles[circles.length-1];
a.radius += 1;
}
//find percentage of canvas filled in
var totalSpace = canvas.width * canvas.height;
var totalFilled = function () {
total = 0;
for (var i = 0; i < circles.length; i++) {
var p = circles[i];
total += Math.PI * Math.pow(p.radius, 2);
}
return total;
console.log(total);
}
function findPercentage() {
return (totalFilled() / totalSpace) * 100;
}
function updateInfo() {
percentage = findPercentage();
document.getElementById("percentage").innerHTML = "You've filled in " + percentage.toFixed(1) + "%";
}
//do all the stuff
var animate = function(){
draw();
grow();
updateInfo();}
var growLoop = 0;
window.onmousedown = function (e) {
// get event location on page offset by canvas location
var location = {
x: e.pageX - offset.x,
y: e.pageY - offset.y
};
if (isOnCanvas(location) && !isOnCircle(location)) {
create(location);
var growLoop = setInterval(animate, 100);
}
};
window.onmouseup = function (e) {
clearInterval(growLoop);
}
window.onmouseout = function (e) {
clearInterval(growLoop);
}
var growLoop = setInterval(animate, 100);
By adding var here you are declaring an internal variable also named growLoop and not assigning to the global. Remove var.
growLoop = setInterval(animate, 100);
http://jsfiddle.net/SeAGU/85/

Categories

Resources