HTML5 Javascript Canvas Collision 2
Tags: javascript, html, canvas
Published: 2022-02-10 11:49:01

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

Финал
Красим шарики под курсором
[jsfiddle.net](https://jsfiddle.net/Spsoi/17wbokau/17/)

```javascript
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();
    }
}
```

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

[jsfiddle.net](https://jsfiddle.net/Spsoi/17wbokau/14/)

```javascript
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();
    }
}
```

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

[jsfiddle.net](https://jsfiddle.net/Spsoi/17wbokau/10/)

```javascript
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();
    }
}
```

Отслеживаем столкновения и пересечения кругов + анимируем
[jsfiddle.net](https://jsfiddle.net/Spsoi/17wbokau/9/)
```javascript
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);// !*
    })
}
```

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

[jsfiddle.net](https://jsfiddle.net/Spsoi/17wbokau/8/)
```javascript
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))
    }
}
```

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

[jsfiddle.net](https://jsfiddle.net/Spsoi/17wbokau/3/)

```javascript

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))
    }
}
```

[jsfiddle.net](https://jsfiddle.net/Spsoi/17wbokau/2/)
Отрисуем 400 кружков

```javascript
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();
```
style.css
```css
canvas {
    border: 1px solid #000;
}
```
index.html
```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>
```