Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas onClick add to array

I got this HTML5 canvas project I'm struggling with. Basically I'm trying to add a new particle on click. And the particles is pushed to the particles array, but the particle does not show. I can see that the particle is pushed to the array with the mouse coordinates, but it doesn't seem like the last particle is drawn. What am I doing wrong?

See example.

// Request animation frame
var requestAnimationFrame = window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame;

// Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

// Set full-screen
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

// Options
var num = 100;              // Number of particles to draw
var size = 2;               // Particle size
var color = '#dd64e6';      // Particle color
var min_speed = .3;         // Particle min speed
var max_speed = 2;          // Particle max speed
var dist = 100; 		    // Max distance before line gets cut
var dist_sq = dist * dist;  // Dist squared
var line_width = 2;         // Line width
var background = '#181b23'; // Background color
var line_color = '#1d2631'; // Line color
var fps = 60;
var now, delta;
var then = Date.now();
var interval = 1000 / fps;

if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
    num = 10;
    fps = 29;
}

// Particles array
var particles = [];
for (var i = 0; i < num; i++) {
    particles.push(
        new create_particle(false, false)
    );
}

// Lets animate the particle
function draw() {

    // Loop
    requestAnimationFrame(draw);

    now = Date.now();
    delta = now - then;

    if (delta > interval) {

        then = now - (delta % interval);

        // Background
        ctx.fillStyle = background;
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // Lets draw particles from the array now
        draw_particles();

    }

}

// Draw particles
function draw_particles() {

    for (var t = 0; t < num; t++) {

        // This particle
        var p = particles[t];

        for (var q = t + 1; q < num; q++) {

            // Check X first, maybe we don't need to
            // calculate Y
            var x = particles[q].x - p.x;
            if ((x *= x) < dist_sq) {

                // Check passed, calculate Y
                var y = particles[q].y - p.y;
                if (x + (y * y) < dist_sq) {

                    // Check passed, draw line
                    draw_line(p.x, p.y, particles[q].x, particles[q].y);

                }

            }

        }

        // Color
        ctx.fillStyle = color;

        // Circle path
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.radius, Math.PI * 2, false);
        ctx.fill();

        // Lets use the velocity now
        p.x += p.vx;
        p.y += p.vy;

        // If there is only 1 particle
        // show X, Y, and velocity
        if (num === 1) {
            ctx.fillText('Y:' + p.y, 20, 20);
            ctx.fillText('X:' + p.x, 20, 40);
            ctx.fillText('YV:' + p.vy, 20, 60);
            ctx.fillText('XV:' + p.vx, 20, 80);
        }

        // To prevent the balls from moving out of the canvas
        if (p.x < size) p.vx *= (p.vx / -p.vx);
        if (p.y < size) p.vy *= (p.vy / -p.vy);
        if (p.x > canvas.width - size) p.vx *= (-p.vx / p.vx);
        if (p.y > canvas.height - size) p.vy *= (-p.vy / p.vy);

    }

}

// Return a particle object
function create_particle(xPos, yPos) {

    // Position
    if (xPos == false && yPos == false) {
        this.x = Math.random() * canvas.width;
        this.y = Math.random() * canvas.height;
    } else {
        this.x = xPos;
        this.y = yPos;
    }

    // Velocity
    this.vx = random_int_between(min_speed, max_speed);
    this.vy = random_int_between(min_speed, max_speed);

    // Size
    this.radius = size;

    console.log('particle created at: ' + this.x + ', ' + this.y);

}

// Returns an random integer, positive or negative
// between the given value
function random_int_between(min, max) {

    var num = Math.floor(Math.random() * max) - min;
    num *= Math.floor(Math.random() * 2) == 1 ? 1 : -1;
    return num;

}

// Draw a line between 2 particles
// given the particles x and y position
function draw_line(p_x, p_y, p2_x, p2_y) {

    ctx.beginPath();
    ctx.lineWidth = line_width;
    ctx.strokeStyle = line_color;
    ctx.moveTo(p_x, p_y);
    ctx.lineTo(p2_x, p2_y);
    ctx.stroke();

}

// When the canvas is clicked
// add new particle
function clicked(e) {

    var mouseXpos, mouseYpos;

    if (e.offsetX) {
        mouseXpos = e.offsetX;
        mouseYpos = e.offsetY;
    } else if (e.layerX) {
        mouseXpos = e.layerX;
        mouseYpos = e.layerY;
    }

    particles.push(
        new create_particle(mouseXpos, mouseYpos)
    );

}

canvas.addEventListener('click', function(e) {
    clicked(e);
}, false);

draw();
<!DOCTYPE html>

<html>

	<head>
		<style>
		* {margin:0;padding:0;overflow:hidden;}
		</style>
	</head>

    <body>

        <canvas id="canvas">{{-- The background --}}</canvas>

    </body>

</html>
like image 607
Kaizokupuffball Avatar asked Apr 14 '17 13:04

Kaizokupuffball


1 Answers

Well since no one else answered this other than in a comment, I thought I would answer it so that others might not wonder about the same thing.

The problem is that you use a variable "num" to hold the number of particles. If you instead use "particles.length" you can

// Request animation frame
var requestAnimationFrame = window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame;

// Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

// Set full-screen
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

// Options
var num = 100;              // Number of particles to draw
var size = 2;               // Particle size
var color = '#dd64e6';      // Particle color
var min_speed = .3;         // Particle min speed
var max_speed = 2;          // Particle max speed
var dist = 100; 		    // Max distance before line gets cut
var dist_sq = dist * dist;  // Dist squared
var line_width = 2;         // Line width
var background = '#181b23'; // Background color
var line_color = '#1d2631'; // Line color
var fps = 60;
var now, delta;
var then = Date.now();
var interval = 1000 / fps;

if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
    num = 10;
    fps = 29;
}

// Particles array
var particles = [];
for (var i = 0; i < num; i++) {
    particles.push(
        new create_particle(false, false)
    );
}

// Lets animate the particle
function draw() {

    // Loop
    requestAnimationFrame(draw);

    now = Date.now();
    delta = now - then;

    if (delta > interval) {

        then = now - (delta % interval);

        // Background
        ctx.fillStyle = background;
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // Lets draw particles from the array now
        draw_particles();

    }

}

// Draw particles
function draw_particles() {

    for (var t = 0; t < particles.length; t++) {

        // This particle
        var p = particles[t];

        for (var q = t + 1; q < particles.length; q++) {

            // Check X first, maybe we don't need to
            // calculate Y
            var x = particles[q].x - p.x;
            if ((x *= x) < dist_sq) {

                // Check passed, calculate Y
                var y = particles[q].y - p.y;
                if (x + (y * y) < dist_sq) {

                    // Check passed, draw line
                    draw_line(p.x, p.y, particles[q].x, particles[q].y);

                }

            }

        }

        // Color
        ctx.fillStyle = color;

        // Circle path
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.radius, Math.PI * 2, false);
        ctx.fill();

        // Lets use the velocity now
        p.x += p.vx;
        p.y += p.vy;

        // If there is only 1 particle
        // show X, Y, and velocity
        if (num === 1) {
            ctx.fillText('Y:' + p.y, 20, 20);
            ctx.fillText('X:' + p.x, 20, 40);
            ctx.fillText('YV:' + p.vy, 20, 60);
            ctx.fillText('XV:' + p.vx, 20, 80);
        }

        // To prevent the balls from moving out of the canvas
        if (p.x < size) p.vx *= (p.vx / -p.vx);
        if (p.y < size) p.vy *= (p.vy / -p.vy);
        if (p.x > canvas.width - size) p.vx *= (-p.vx / p.vx);
        if (p.y > canvas.height - size) p.vy *= (-p.vy / p.vy);

    }

}

// Return a particle object
function create_particle(xPos, yPos) {

    // Position
    if (xPos == false && yPos == false) {
        this.x = Math.random() * canvas.width;
        this.y = Math.random() * canvas.height;
    } else {
        this.x = xPos;
        this.y = yPos;
    }

    // Velocity
    this.vx = random_int_between(min_speed, max_speed);
    this.vy = random_int_between(min_speed, max_speed);

    // Size
    this.radius = size;

    console.log('particle created at: ' + this.x + ', ' + this.y);

}

// Returns an random integer, positive or negative
// between the given value
function random_int_between(min, max) {

    var num = Math.floor(Math.random() * max) - min;
    num *= Math.floor(Math.random() * 2) == 1 ? 1 : -1;
    return num;

}

// Draw a line between 2 particles
// given the particles x and y position
function draw_line(p_x, p_y, p2_x, p2_y) {

    ctx.beginPath();
    ctx.lineWidth = line_width;
    ctx.strokeStyle = line_color;
    ctx.moveTo(p_x, p_y);
    ctx.lineTo(p2_x, p2_y);
    ctx.stroke();

}

// When the canvas is clicked
// add new particle
function clicked(e) {

    var mouseXpos, mouseYpos;

    if (e.offsetX) {
        mouseXpos = e.offsetX;
        mouseYpos = e.offsetY;
    } else if (e.layerX) {
        mouseXpos = e.layerX;
        mouseYpos = e.layerY;
    }

    particles.push(
        new create_particle(mouseXpos, mouseYpos)
    );

}

canvas.addEventListener('click', function(e) {
    clicked(e);
}, false);

draw();
<!DOCTYPE html>

<html>

	<head>
		<style>
		* {margin:0;padding:0;overflow:hidden;}
		</style>
	</head>

    <body>

        <canvas id="canvas">{{-- The background --}}</canvas>

    </body>

</html>

I will dare to go outside the scope of our problem because you could prevent such issues in the future by utilizing Array.prototype.forEach, and move the drawing of the dots and constrain them to new functions. With the added benefit of simplifying your code.

// Draw particles
function draw_particles() {
    particles.forEach(function(p,pi){
        particles.forEach(function(p2,p2i){
            if(pi === p2i){
                return;
            }
            // Check X first, maybe we don't need to
            // calculate Y
            var x = p2.x - p.x;
            if ((x *= x) < dist_sq) {
                // Check passed, calculate Y
                var y = p2.y - p.y;
                if (x + (y * y) < dist_sq) {
                    // Check passed, draw line
                    draw_line(p.x, p.y, p2.x, p2.y);
                    draw_dot(p);
                    constrain(p);
                }
            }
        });

    });
}

// Draw particle
function draw_dot(p){
    // Color
    ctx.fillStyle = color;

    // Circle path
    ctx.beginPath();
    ctx.arc(p.x, p.y, p.radius, Math.PI * 2, false);
    ctx.fill();
    ctx.closePath();

    // Lets use the velocity now
    p.x += p.vx;
    p.y += p.vy;

    // If there is only 1 particle
    // show X, Y, and velocity
    if (particles.length === 1) {
        ctx.fillText('Y:' + p.y, 20, 20);
        ctx.fillText('X:' + p.x, 20, 40);
        ctx.fillText('YV:' + p.vy, 20, 60);
        ctx.fillText('XV:' + p.vx, 20, 80);
    }
}

// Constrain particle movement
function constrain(p){
    // To prevent the balls from moving out of the canvas
    if (p.x < size) p.vx *= (p.vx / -p.vx);
    if (p.y < size) p.vy *= (p.vy / -p.vy);
    if (p.x > canvas.width - size) p.vx *= (-p.vx / p.vx);
    if (p.y > canvas.height - size) p.vy *= (-p.vy / p.vy);
}

Using Array.prototype.length and Array.prototype.forEach reduces the risk of heading into issues of array indices.

like image 73
Espen Avatar answered Nov 13 '22 21:11

Espen