DEV Community

Spsoi
Spsoi

Posted on

2 1

HTML5 Javascript Canvas Collision 2

Читать сверху вниз
Будет пополнятся

Финал
Красим шарики под курсором
jsfiddle.net

function Particle (x, y, radius, color) { // шарик
    // 2
    this.x =x;
    this.y = y;
    this.radius = radius;
    this.color = randomColor(colors);// !*
    this.mass = 1;
    this.opacity = 0; // !* // Прозрачность
    this.velocity = { 
        x: (Math.random() - 0.5) * 5,// !*
        y: (Math.random() - 0.5) * 5,// !*
    }

    this.update = particles => { 
        this.draw();

        for (let i = 0; i < particles.length; i++) {
            if (this === particles[i]) { continue }
            if (getDistance(this.x, this.y, particles[i].x, particles[i].y) - this.radius * 2 < 0) {
                resolveCollision(this, particles[i]); // !*
            }
        }

        if (this.x - this.radius <= 0 || 
            this.x + this.radius >= innerWidth) { 
                this.velocity.x = -this.velocity.x;
        }

        if (this.y - this.radius <= 0 ||
            this.y + this.radius >= innerHeight) { 
                this.velocity.y = -this.velocity.y;
        }

        // В консоль что шарик под курсором мыши.
        if (getDistance(mouse.x, mouse.y, this.x, this.y) < 120 && this.opacity < 0.2) { //*!
            this.opacity += 0.02;
            console.log('Под курсором'); 
        }else if (this.opacity > 0) {
            this.opacity -= 0.02;
            this.opacity = Math.max(0, this.opacity);
        }

        this.x += this.velocity.x;
        this.y += this.velocity.y;
    }

    this.draw = function () {
        c.beginPath();
            c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
            c.save(); //*! // сохраняем состаяние
                c.globalAlpha = this.opacity;
                c.fillStyle = this.color;
                c.fill(); //*!
            c.restore();//*! // востанавливаем состояние после c.save()
            c.strokeStyle = this.color; //*!
            c.stroke();
        c.closePath();
    }
}
Enter fullscreen mode Exit fullscreen mode

Отскок не только от края но и от других частиц, кругов и т.д

jsfiddle.net

function rotate(velocity, angle) {  // !*
    const rotatedVelocities = {
        x: velocity.x * Math.cos(angle) - velocity.y * Math.sin(angle),
        y: velocity.x * Math.sin(angle) + velocity.y * Math.cos(angle)
    };

    return rotatedVelocities;
}

function resolveCollision(particle, otherParticle) {  // !*
    const xVelocityDiff = particle.velocity.x - otherParticle.velocity.x;
    const yVelocityDiff = particle.velocity.y - otherParticle.velocity.y;

    const xDist = otherParticle.x - particle.x;
    const yDist = otherParticle.y - particle.y;

    // Предотвращение случайного наложения частиц
    if (xVelocityDiff * xDist + yVelocityDiff * yDist >= 0) {

        // Угол захвата между двумя сталкивающимися частицами
        const angle = -Math.atan2(otherParticle.y - particle.y, otherParticle.x - particle.x);

        // Сохраняем массу в переменную для лучшей читаемости в уравнении столкновения
        const m1 = particle.mass;
        const m2 = otherParticle.mass;

        // Скорость до уравнения
        const u1 = rotate(particle.velocity, angle);
        const u2 = rotate(otherParticle.velocity, angle);

        // Скорость после 1d уравнения столкновения
        const v1 = { x: u1.x * (m1 - m2) / (m1 + m2) + u2.x * 2 * m2 / (m1 + m2), y: u1.y };
        const v2 = { x: u2.x * (m1 - m2) / (m1 + m2) + u1.x * 2 * m2 / (m1 + m2), y: u2.y };

        // Конечная скорость после поворота оси обратно в исходное положение
        const vFinal1 = rotate(v1, -angle);
        const vFinal2 = rotate(v2, -angle);

        // Поменяй местами скорости частиц для реалистичного эффекта отскока
        particle.velocity.x = vFinal1.x;
        particle.velocity.y = vFinal1.y;

        otherParticle.velocity.x = vFinal2.x;
        otherParticle.velocity.y = vFinal2.y;
    }
}


function Particle (x, y, radius, color) { // шарик
    // 2
    this.x =x;
    this.y = y;
    this.radius = radius;
    this.color = color;
    this.mass = 1;
    this.velocity = { 
        x: Math.random() - 0.5,
        y: Math.random() - 0.5,
    }

    this.update = particles => { 
        this.draw();

        for (let i = 0; i < particles.length; i++) {
            if (this === particles[i]) { continue }
            if (getDistance(this.x, this.y, particles[i].x, particles[i].y) - this.radius * 2 < 0) {
                resolveCollision(this, particles[i]); // !*
            }
        }

        if (this.x - this.radius <= 0 || 
            this.x + this.radius >= innerWidth) { 
                this.velocity.x = -this.velocity.x;
        }

        if (this.y - this.radius <= 0 ||
            this.y + this.radius >= innerHeight) { 
                this.velocity.y = -this.velocity.y;
        }

        this.x += this.velocity.x;
        this.y += this.velocity.y;
    }

    this.draw = function () {
        c.beginPath();
            c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
            c.fillStyle = this.color;
            // c.fill(); //*!
            c.stroke();
        c.closePath();
    }
}
Enter fullscreen mode Exit fullscreen mode

Отскок от краёв экрана

jsfiddle.net

function Particle (x, y, radius, color) { // шарик
    // 2
    this.x =x;
    this.y = y;
    this.radius = radius;
    this.color = color;
    this.velocity = { 
        x: Math.random() - 0.5,
        y: Math.random() - 0.5,
    }

    this.update = particles => { 
        this.draw();

        for (let i = 0; i < particles.length; i++) {
            if (this === particles[i]) { continue }
            if (getDistance(this.x, this.y, particles[i].x, particles[i].y) - this.radius * 2 < 0) {
                console.log('has collided');
            }
        }

        if (this.x - this.radius <= 0 || // !*
            this.x + this.radius >= innerWidth) { 
                this.velocity.x = -this.velocity.x;
        }

        if (this.y - this.radius <= 0 || // !*
            this.y + this.radius >= innerHeight) { 
                this.velocity.y = -this.velocity.y;
        }

        this.x += this.velocity.x;
        this.y += this.velocity.y;
    }

    this.draw = function () {
        c.beginPath();
            c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
            c.fillStyle = this.color;
            // c.fill(); //*!
            c.stroke();
        c.closePath();
    }
}
Enter fullscreen mode Exit fullscreen mode

Отслеживаем столкновения и пересечения кругов + анимируем
jsfiddle.net

function Particle (x, y, radius, color) { // шарик
    // 2
    this.x =x;
    this.y = y;
    this.radius = radius;
    this.color = color;
    this.velocity = { // !*
        x: Math.random() - 0.5,
        y: Math.random() - 0.5,
    }

    this.update = particles => { // !*
        this.draw();

        for (let i = 0; i < particles.length; i++) {// !*
            if (this === particles[i]) { continue }
            if (getDistance(this.x, this.y, particles[i].x, particles[i].y) - this.radius * 2 < 0) {
                console.log('has collided');
            }
        }

        this.x += this.velocity.x;// !*
        this.y += this.velocity.y;// !*
    }

    this.draw = function () {
        c.beginPath();
            c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
            c.fillStyle = this.color;
            // c.fill(); //*!
            c.stroke();
        c.closePath();
    }
}

function animate() {
    requestAnimationFrame(animate);
    c.clearRect(0,0, canvas.width, canvas.height);
    particles.forEach(particle => { // !*
        particle.update(particles);// !*
    })
}
Enter fullscreen mode Exit fullscreen mode

Круги теперь не уходят за экран canvas на половину.

jsfiddle.net

function init() { // точка входа
    particles = []; 
    //1
    for (let i = 0; i < 4; i++) { 
        const radius = 100;
        let x = randomIntFromRange(radius, canvas.width - radius); // !*
        let y = randomIntFromRange(radius, canvas.height - radius); // !*
        const color = 'blue';

        if ( i != 0 ) { // Если идёт пересечение кругов, отрисуй в другом месте. 
            for (let j = 0; j < particles.length; j++ ) {
                if (getDistance(x, y, particles[j].x, particles[j].y) - radius * 2 < 0) {
                    x = randomIntFromRange(radius, canvas.width - radius); // !*
                    y = randomIntFromRange(radius, canvas.height - radius); // !*

                    j = -1;
                }
            }
        }

        particles.push (new Particle(x, y, radius, color))
    }
}
Enter fullscreen mode Exit fullscreen mode

Отрисуем 4 круга, если идёт пересечение кругов, отрисовать этот круг в другом месте.

jsfiddle.net


function getDistance(x1, y1, x2, y2) { // расчёт дистанции до центра кругов
    let xDistance = x2 -x1; 
    let yDistance = y2 -y1; 

    let distance = Math.pow(xDistance, 2) + Math.pow(yDistance, 2); // Теорема Пифагора

    return Math.sqrt(distance); // квадратный корень числа
}

function init() { // точка входа
    particles = []; 
    //1
    for (let i = 0; i < 4; i++) { 
        let x = Math.random() * innerWidth;
        let y = Math.random() * innerHeight;
        const radius = 100;
        const color = 'blue';

        if ( i != 0 ) { // !* // Если идёт пересечение кругов, отрисуй в другом месте. 
            for (let j = 0; j < particles.length; j++ ) {
                if (getDistance(x, y, particles[j].x, particles[j].y) - radius * 2 < 0) {
                    x = Math.random() * innerWidth;
                    y = Math.random() * innerHeight;

                    j = -1;
                }
            }
        }

        particles.push (new Particle(x, y, radius, color))
    }
}
Enter fullscreen mode Exit fullscreen mode

jsfiddle.net
Отрисуем 400 кружков

canvas = document.querySelector('#canvas');
let c = canvas.getContext('2d');

canvas.width = innerWidth;
canvas.height = innerHeight;

let mouse = {
    x: innerWidth / 2,
    y: innerHeight / 2
}

let colors = [
    '#2185c5',
    '#7ECEFD',
    '#FFF6E5',
    '#FF7F66'
];

document.addEventListener("mousemove", function(event){
    mouse.x = event.clientX;
    mouse.y = event.clientY;
});

addEventListener("resize", function(){
    console.log()
    canvas.width = innerWidth;
    canvas.height = innerHeight;
    // init();
});

function randomIntFromRange(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function randomColor (color){
    return color[Math.floor(Math.random() * color.length)];
}

addEventListener("click", function () {
    init();
})


function Particle (x, y, radius, color) { // шарик
    // 2
    this.x =x;
    this.y = y;
    this.radius = radius;
    this.color = color;

    this.update = function() {
        this.draw();
    }

    this.draw = function () {
        c.beginPath();
            c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
            c.fillStyle = this.color;
            c.fill();
            c.stroke();
        c.closePath();
    }
}

let particles; //!*

function init() { // точка входа
    particles = []; //!*
    //1
    for (let i = 0; i < 400; i++) { //!*
        const x = Math.random() * innerWidth;
        const y = Math.random() * innerHeight;
        const radius = 10;
        const color = 'blue';
        particles.push (new Particle(x, y, radius, color))
    }
}

function animate() {
    requestAnimationFrame(animate);
    c.clearRect(0,0, canvas.width, canvas.height);
    particles.forEach(object => { //!*
        object.update();
    })
}

init();
animate();
Enter fullscreen mode Exit fullscreen mode

style.css

canvas {
    border: 1px solid #000;
}
Enter fullscreen mode Exit fullscreen mode

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Document</title>
</head>
<body>
    <canvas id="canvas" width="300" height="300"></canvas>
    <script  src="canvas.js" ></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more