目前,我正在创建一种物理模拟,在这种模拟中,一群不同半径的球相互反弹,相互撞击墙壁.为了在我重构之前让它看起来更好,我想让球在碰撞时改变 colored颜色 ,但我不确定我应该把它添加到哪里,或者如何添加.

注: colored颜色 应随机化,使用

randomColor = Math.floor(Math.random() * 16777215).toString(16);

color = "#" + randomColor;

注意:只有发生碰撞的球才会变色.因此,如果两个球接触,它们应该都会改变 colored颜色 ,但不会改变其他 colored颜色 .

我怎么才能让球在每次碰撞后都会变色?

function lineMessage(msg) {
    document.querySelector('#myMessage').textContent += msg + '. ';
}

function groupMessage(msg) {
    document.querySelector('#myMessage').innerHTML += msg + '<br/>';
}

const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext("2d");
canvas.width = 1000;
canvas.height = 550;
const ballCount = document.querySelector('#ball-count');

const gravity = 0;
const wallLoss = 1;
let numBalls = 0;  // approx as will not add ball if space can not be found
const minBallSize = 10;
const maxBallSize = 100;
const velMin = 1;
const velMax = 5; 
const maxResolutionCycles = 100;

Math.TAU = Math.PI * 2;
Math.rand = (min, max) => Math.random() * (max - min) + min;
Math.randI = (min, max) => Math.random() * (max - min) + min | 0; // only for positive numbers 32bit signed int
Math.randItem = arr => arr[Math.random() * arr.length | 0]; // only for arrays with length < 2 ** 31 - 1

//contact points of two circles radius r1, r2 moving along two lines (a,e)-(b,f) and (c,g)-(d,h)
Math.circlesInterceptUnitTime = (a, e, b, f, c, g, d, h, r1, r2) => {
    const A = a * a, B = b * b, C = c * c, D = d * d;
    const E = e * e, F = f * f, G = g * g, H = h * h;
    var R = (r1 + r2) ** 2;
    const AA = A + B + C + F + G + H + D + E + b * c + c * b + f * g + g * f + 2 * (a * d - a * b - a * c - b * d - c * d - e * f + e * h - e * g - f * h - g * h);
    const BB = 2 * (-A + a * b + 2 * a * c - a * d - c * b - C + c * d - E + e * f + 2 * e * g - e * h - g * f - G + g * h);
    const CC = A - 2 * a * c + C + E - 2 * e * g + G - R;
    return Math.quadRoots(AA, BB, CC);
}  

Math.quadRoots = (a, b, c) => { // find roots for quadratic
    if (Math.abs(a) < 1e-6) {
        return b != 0 ? [-c / b] : [] 
    }

    b /= a;
    var d = b * b - 4 * (c / a);

    if (d > 0) {
        d = d ** 0.5;
        return  [0.5 * (-b + d), 0.5 * (-b - d)]
    }

    return d === 0 ? [0.5 * -b] : [];
}

Math.interceptLineBallTime = (x, y, vx, vy, x1, y1, x2, y2, r) => {
    const xx = x2 - x1;
    const yy = y2 - y1;
    const d = vx * yy - vy * xx;

    if (d > 0) {  // only if moving towards the line
        const dd = r / (xx * xx + yy * yy) ** 0.5;
        const nx = xx * dd;
        const ny = yy * dd;
        return (xx * (y - (y1 + nx)) - yy * (x - (x1 - ny))) / d;
    }
}

const balls = [];
const lines = [];

function Line(x1, y1, x2, y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
}

Line.prototype = {
    draw() {
        ctx.moveTo(this.x1, this.y1);
        ctx.lineTo(this.x2, this.y2);
    },
    reverse() {
        const x = this.x1;
        const y = this.y1;
        this.x1 = this.x2;
        this.y1 = this.y2;
        this.x2 = x;
        this.y2 = y;
        return this;
    }
}
        
function Ball(x, y, vx, vy, r = 45, m = 4 / 3 * Math.PI * (r ** 3)) {
    this.r = r;
    this.m = m;
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
}

Ball.prototype = {
    update() {
        this.x += this.vx;
        this.y += this.vy;
        this.vy += gravity;
    },
    draw() {
        ctx.moveTo(this.x + this.r, this.y);
        ctx.arc(this.x, this.y, this.r, 0, Math.TAU);
    },
    interceptLineTime(l, time) {
        const u = Math.interceptLineBallTime(this.x, this.y, this.vx, this.vy, l.x1, l.y1, l.x2, l.y2, this.r);

        if (u >= time && u <= 1) {
            return u;
        }
    },
    checkBallBallTime(t, minTime) {
        return t > minTime && t <= 1;
    },
    interceptBallTime(b, time) {
        const x = this.x - b.x;
        const y = this.y - b.y;
        const d = (x * x + y * y) ** 0.5;

        if (d > this.r + b.r) {
            const times = Math.circlesInterceptUnitTime(
                this.x, this.y, 
                this.x + this.vx, this.y + this.vy, 
                b.x, b.y,
                b.x + b.vx, b.y + b.vy, 
                this.r, b.r
            )

            if (times.length) {
                if (times.length === 1) {
                    if (this.checkBallBallTime(times[0], time)) {
                        return times[0]
                    }

                    return;
                }

                if (times[0] <= times[1]) {
                    if (this.checkBallBallTime(times[0], time)) {
                        return times[0]
                    }

                    if (this.checkBallBallTime(times[1], time)) {
                        return times[1]
                    }

                    return
                }

                if (this.checkBallBallTime(times[1], time)) { 
                    return times[1]
                }      

                if (this.checkBallBallTime(times[0], time)) {
                    return times[0]
                }
            }
        }
    },
    collideLine(l, time) {
        const x1 = l.x2 - l.x1;
        const y1 = l.y2 - l.y1;
        const d = (x1 * x1 + y1 * y1) ** 0.5;
        const nx = x1 / d;
        const ny = y1 / d;            
        const u = (this.vx  * nx + this.vy  * ny) * 2;
        this.x += this.vx * time;   
        this.y += this.vy * time;   
        this.vx = (nx * u - this.vx) * wallLoss;
        this.vy = (ny * u - this.vy) * wallLoss;
        this.x -= this.vx * time;
        this.y -= this.vy * time;
    },
    collide(b, time) {
        const a = this;
        const m1 = a.m;
        const m2 = b.m;
        const x = a.x - b.x
        const y = a.y - b.y  
        const d = (x * x + y * y);
        const u1 = (a.vx * x + a.vy * y) / d
        const u2 = (x * a.vy - y * a.vx ) / d
        const u3 = (b.vx * x + b.vy * y) / d
        const u4 = (x * b.vy - y * b.vx ) / d
        const mm = m1 + m2;
        const vu3 = (m1 - m2) / mm * u1 + (2 * m2) / mm * u3;
        const vu1 = (m2 - m1) / mm * u3 + (2 * m1) / mm * u1;
        a.x = a.x + a.vx * time;
        a.y = a.y + a.vy * time;
        b.x = b.x + b.vx * time;
        b.y = b.y + b.vy * time;
        b.vx = x * vu1 - y * u4;
        b.vy = y * vu1 + x * u4;
        a.vx = x * vu3 - y * u2;
        a.vy = y * vu3 + x * u2;
        a.x = a.x - a.vx * time;
        a.y = a.y - a.vy * time;
        b.x = b.x - b.vx * time;
        b.y = b.y - b.vy * time;
    },
    doesOverlap(ball) {
        const x = this.x - ball.x;
        const y = this.y - ball.y;
        return  (this.r + ball.r) > ((x * x + y * y) ** 0.5);  
    }       
}

function canAdd(ball) {
    for (const b of balls) {
        if (ball.doesOverlap(b)) {
            return false
        }
    }

    return true;
}

function create(bCount) {
    //Instead of doing bCount = 1, you can also do create(1)
    bCount = 1;
    lines.push(new Line(-10, 10, ctx.canvas.width + 10, 5));
    lines.push((new Line(-10, ctx.canvas.height - 2, ctx.canvas.width + 10, ctx.canvas.height - 10)).reverse());
    lines.push((new Line(10, -10, 4, ctx.canvas.height + 10)).reverse());
    lines.push(new Line(ctx.canvas.width - 3, -10, ctx.canvas.width - 10, ctx.canvas.height + 10)); 

    while (bCount--) {
        let tries = 100;
        debugger

        while (tries--) {
            const dir = Math.rand(0, Math.TAU);
            const vel = Math.rand(velMin, velMax);
            const ball = new Ball(
                Math.rand(maxBallSize + 10, canvas.width - maxBallSize - 10), 
                Math.rand(maxBallSize + 10, canvas.height - maxBallSize - 10),
                Math.cos(dir) * vel,
                Math.sin(dir) * vel,
                Math.rand(minBallSize, maxBallSize),
            )

            if (canAdd(ball)) {
                balls.push(ball);
                break;
            }
        }
    }
}

function resolveCollisions() {
    var minTime = 0, minObj, minBall, resolving = true, idx = 0, idx1, after = 0, e = 0;
        
    while (resolving && e++ < maxResolutionCycles) { // too main ball may create very lone resolution cycle. e limits this
        resolving = false;
        minObj = undefined;
        minBall = undefined;
        minTime = 1;
        idx = 0;

        for(const b of balls) {
            idx1 = idx + 1;
            while (idx1 < balls.length) {
                const b1 = balls[idx1++];
                const time = b.interceptBallTime(b1, after);

                if (time !== undefined) {
                    if (time <= minTime) {
                         minTime = time;
                        minObj = b1;
                        minBall = b;
                        resolving = true;
                    }
                }
            }

            for (const l of lines) {
                 const time = b.interceptLineTime(l, after);
                if (time !== undefined) {
                    if (time <= minTime) {
                        minTime = time;
                        minObj = l;
                        minBall = b;
                        resolving = true;
                    }
                }
            }

            idx++;
        }

        if (resolving) {
            if (minObj instanceof Ball) {
                minBall.collide(minObj, minTime);
            } else {
                minBall.collideLine(minObj, minTime);
            }

            after = minTime;
        }
    }
}

function mainLoop() {
    ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
    resolveCollisions();

    for (const b of balls) {
        b.update()
    }

    ctx.strokeStyle = "#000";
    ctx.beginPath();

    for (const b of balls) {
        b.draw()
    }

    for (const l of lines) {
        l.draw()
    }

    ctx.stroke();
    requestAnimationFrame(mainLoop);
}    

function addBall() {
    numBalls++;
    ballCount.innerHTML = numBalls;
    //Instead of doing create(numBalls), you can do create(1)
    create(numBalls);
}

mainLoop();
#canvas {
    width: 1000px;
    height: 550px
}

#myConsole {
    background-color: black;
    color: white;
    min-height: 100px;
}
<!DOCTYPE html>
<html lang="en">

<html>
    <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">
        <meta name="author" content="Christian Davis">
        <link rel="stylesheet" href="styles.css">

        <title>Bouncy Balls</title>
    </head>

    <body>
        <button onclick="addBall()">Add Ball</button><br>
        <div>Ball Count: <span id="ball-count">0</span></div>
        <canvas id="canvas"></canvas>
        <p id="myConsole">&gt;&nbsp;<span id="myMessage"></span></p>

        <script src="app2.js"></script>
    </body>
</html>

推荐答案

此代码为您刚刚创建的球生成一种 colored颜色 :

const randomColor = Math.floor(Math.random() * 16777215).toString(16);
    a.color = "#" + randomColor;
    b.color = "#" + randomColor;

这里,在该函数Ball(....)中,当两个或更多个球碰撞时,代码生成新的 colored颜色 :

this.color = "#" + Math.floor(Math.random() * 16777215).toString(16);

以下是正确的版本:

function lineMessage(msg) {
    document.querySelector('#myMessage').textContent += msg + '. ';
}

function groupMessage(msg) {
    document.querySelector('#myMessage').innerHTML += msg + '<br/>';
}

const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext("2d");
canvas.width = 1000;
canvas.height = 550;
const ballCount = document.querySelector('#ball-count');

const gravity = 0;
const wallLoss = 1;
let numBalls = 0;
const minBallSize = 10;
const maxBallSize = 100;
const velMin = 1;
const velMax = 5;
const maxResolutionCycles = 100;

Math.TAU = Math.PI * 2;
Math.rand = (min, max) => Math.random() * (max - min) + min;
Math.randI = (min, max) => Math.random() * (max - min) + min | 0;
Math.randItem = arr => arr[Math.random() * arr.length | 0];

Math.circlesInterceptUnitTime = (a, e, b, f, c, g, d, h, r1, r2) => {
    const A = a * a, B = b * b, C = c * c, D = d * d;
    const E = e * e, F = f * f, G = g * g, H = h * h;
    var R = (r1 + r2) ** 2;
    const AA = A + B + C + F + G + H + D + E + b * c + c * b + f * g + g * f + 2 * (a * d - a * b - a * c - b * d - c * d - e * f + e * h - e * g - f * h - g * h);
    const BB = 2 * (-A + a * b + 2 * a * c - a * d - c * b - C + c * d - E + e * f + 2 * e * g - e * h - g * f - G + g * h);
    const CC = A - 2 * a * c + C + E - 2 * e * g + G - R;
    return Math.quadRoots(AA, BB, CC);
}

Math.quadRoots = (a, b, c) => {
    if (Math.abs(a) < 1e-6) {
        return b != 0 ? [-c / b] : [];
    }

    b /= a;
    var d = b * b - 4 * (c / a);

    if (d > 0) {
        d = d ** 0.5;
        return [0.5 * (-b + d), 0.5 * (-b - d)];
    }

    return d === 0 ? [0.5 * -b] : [];
}

Math.interceptLineBallTime = (x, y, vx, vy, x1, y1, x2, y2, r) => {
    const xx = x2 - x1;
    const yy = y2 - y1;
    const d = vx * yy - vy * xx;

    if (d > 0) {
        const dd = r / (xx * xx + yy * yy) ** 0.5;
        const nx = xx * dd;
        const ny = yy * dd;
        return (xx * (y - (y1 + nx)) - yy * (x - (x1 - ny))) / d;
    }
}

const balls = [];
const lines = [];

function Line(x1, y1, x2, y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
}

Line.prototype = {
    draw() {
        ctx.moveTo(this.x1, this.y1);
        ctx.lineTo(this.x2, this.y2);
    },
    reverse() {
        const x = this.x1;
        const y = this.y1;
        this.x1 = this.x2;
        this.y1 = this.y2;
        this.x2 = x;
        this.y2 = y;
        return this;
    }
}

function Ball(x, y, vx, vy, r = 45, m = 4 / 3 * Math.PI * (r ** 3)) {
    this.r = r;
    this.m = m;
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
    this.color = "#" + Math.floor(Math.random() * 16777215).toString(16); // Initialize with a random color
}

Ball.prototype = {
    update() {
        this.x += this.vx;
        this.y += this.vy;
        this.vy += gravity;
    },
    draw() {
        ctx.fillStyle = this.color; // Set the fill color to the ball's color
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r, 0, Math.TAU);
        ctx.fill();
    },
    interceptLineTime(l, time) {
        const u = Math.interceptLineBallTime(this.x, this.y, this.vx, this.vy, l.x1, l.y1, l.x2, l.y2, this.r);

        if (u >= time && u <= 1) {
            return u;
        }
    },
    checkBallBallTime(t, minTime) {
        return t > minTime && t <= 1;
    },
    interceptBallTime(b, time) {
        const x = this.x - b.x;
        const y = this.y - b.y;
        const d = (x * x + y * y) ** 0.5;

        if (d > this.r + b.r) {
            const times = Math.circlesInterceptUnitTime(
                this.x, this.y,
                this.x + this.vx, this.y + this.vy,
                b.x, b.y,
                b.x + b.vx, b.y + b.vy,
                this.r, b.r
            )

            if (times.length) {
                if (times.length === 1) {
                    if (this.checkBallBallTime(times[0], time)) {
                        return times[0]
                    }

                    return;
                }

                if (times[0] <= times[1]) {
                    if (this.checkBallBallTime(times[0], time)) {
                        return times[0]
                    }

                    if (this.checkBallBallTime(times[1], time)) {
                        return times[1]
                    }

                    return
                }

                if (this.checkBallBallTime(times[1], time)) {
                    return times[1]
                }

                if (this.checkBallBallTime(times[0], time)) {
                    return times[0]
                }
            }
        }
    },
    collideLine(l, time) {
        const x1 = l.x2 - l.x1;
        const y1 = l.y2 - l.y1;
        const d = (x1 * x1 + y1 * y1) ** 0.5;
        const nx = x1 / d;
        const ny = y1 / d;
        const u = (this.vx * nx + this.vy * ny) * 2;
        this.x += this.vx * time;
        this.y += this.vy * time;
        this.vx = (nx * u - this.vx) * wallLoss;
        this.vy = (ny * u - this.vy) * wallLoss;
        this.x -= this.vx * time;
        this.y -= this.vy * time;
    },
    collide(b, time) {
        const a = this;
        const m1 = a.m;
        const m2 = b.m;
        const x = a.x - b.x;
        const y = a.y - b.y;
        const d = (x * x + y * y);
        const u1 = (a.vx * x + a.vy * y) / d;
        const u2 = (x * a.vy - y * a.vx) / d;
        const u3 = (b.vx * x + b.vy * y) / d;
        const u4 = (x * b.vy - y * b.vx) / d;
        const mm = m1 + m2;
        const vu3 = (m1 - m2) / mm * u1 + (2 * m2) / mm * u3;
        const vu1 = (m2 - m1) / mm * u3 + (2 * m1) / mm * u1;
        a.x = a.x + a.vx * time;
        a.y = a.y + a.vy * time;
        b.x = b.x + b.vx * time;
        b.y = b.y + b.vy * time;
        b.vx = x * vu1 - y * u4;
        b.vy = y * vu1 + x * u4;
        a.vx = x * vu3 - y * u2;
        a.vy = y * vu3 + x * u2;
        a.x = a.x - a.vx * time;
        a.y = a.y - a.vy * time;
        b.x = b.x - b.vx * time;
        b.y = b.y - b.vy * time;

        // Change color upon collision
        const randomColor = Math.floor(Math.random() * 16777215).toString(16);
        a.color = "#" + randomColor;
        b.color = "#" + randomColor;
    },
    doesOverlap(ball) {
        const x = this.x - ball.x;
        const y = this.y - ball.y;
        return (this.r + ball.r) > ((x * x + y * y) ** 0.5);
    }
}

function canAdd(ball) {
    for (const b of balls) {
        if (ball.doesOverlap(b)) {
            return false
        }
    }

    return true;
}

function create(bCount) {
    bCount = 1;
    lines.push(new Line(-10, 10, ctx.canvas.width + 10, 5));
    lines.push((new Line(-10, ctx.canvas.height - 2, ctx.canvas.width + 10, ctx.canvas.height - 10)).reverse());
    lines.push((new Line(10, -10, 4, ctx.canvas.height + 10)).reverse());
    lines.push(new Line(ctx.canvas.width - 3, -10, ctx.canvas.width - 10, ctx.canvas.height + 10));

    while (bCount--) {
        let tries = 100;
        debugger

        while (tries--) {
            const dir = Math.rand(0, Math.TAU);
            const vel = Math.rand(velMin, velMax);
            const ball = new Ball(
                Math.rand(maxBallSize + 10, canvas.width - maxBallSize - 10),
                Math.rand(maxBallSize + 10, canvas.height - maxBallSize - 10),
                Math.cos(dir) * vel,
                Math.sin(dir) * vel,
                Math.rand(minBallSize, maxBallSize),
            )

            if (canAdd(ball)) {
                balls.push(ball);
                break;
            }
        }
    }
}

function resolveCollisions() {
    var minTime = 0, minObj, minBall, resolving = true, idx = 0, idx1, after = 0, e = 0;

    while (resolving && e++ < maxResolutionCycles) {
        resolving = false;
        minObj = undefined;
        minBall = undefined;
        minTime = 1;
        idx = 0;

        for (const b of balls) {
            idx1 = idx + 1;
            while (idx1 < balls.length) {
                const b1 = balls[idx1++];
                const time = b.interceptBallTime(b1, after);

                if (time !== undefined) {
                    if (time <= minTime) {
                        minTime = time;
                        minObj = b1;
                        minBall = b;
                        resolving = true;
                    }
                }
            }

            for (const l of lines) {
                const time = b.interceptLineTime(l, after);
                if (time !== undefined) {
                    if (time <= minTime) {
                        minTime = time;
                        minObj = l;
                        minBall = b;
                        resolving = true;
                    }
                }
            }

            idx++;
        }

        if (resolving) {
            if (minObj instanceof Ball) {
                minBall.collide(minObj, minTime);
            } else {
                minBall.collideLine(minObj, minTime);
            }

            after = minTime;
        }
    }
}

function mainLoop() {
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    resolveCollisions();

    for (const b of balls) {
        b.update()
    }

    ctx.strokeStyle = "#000";
    ctx.beginPath();

    for (const b of balls) {
        b.draw()
    }

    for (const l of lines) {
        l.draw()
    }

    ctx.stroke();
    requestAnimationFrame(mainLoop);
}

function addBall() {
    numBalls++;
    ballCount.innerHTML = numBalls;
    create(1);
}

mainLoop();
#canvas {
    width: 1000px;
    height: 550px
}

#myConsole {
    background-color: black;
    color: white;
    min-height: 100px;
}
<!DOCTYPE html>
<html lang="en">

<html>
    <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">
        <meta name="author" content="Christian Davis">
        <link rel="stylesheet" href="styles.css">

        <title>Bouncy Balls</title>
    </head>

    <body>
        <button onclick="addBall()">Add Ball</button><br>
        <div>Ball Count: <span id="ball-count">0</span></div>
        <canvas id="canvas"></canvas>
        <p id="myConsole">&gt;&nbsp;<span id="myMessage"></span></p>

        <script src="app2.js"></script>
    </body>
</html>

Javascript相关问答推荐

Cypress -使用commands.js将数据测试id串在一起失败,但在将它们串在一起时不使用命令有效

jQuery提交按钮重新加载页面,即使在WordPress中使用preventDefault()

PrivateRoute不是路由组件错误

为什么!逗号和空格不会作为输出返回,如果它在参数上?

无法从NextJS组件传递函数作为参数'

为什么useState触发具有相同值的呈现

Chart.js-显示值应该在其中的引用区域

为什么123[';toString';].long返回1?

对网格项目进行垂直排序不起作用

确保函数签名中的类型安全:匹配值

Webpack在导入前混淆文件名

使用父标签中的Find函数查找元素

react -原生向量-图标笔划宽度

使用Reaction窗体挂钩注册日历组件

JAVASCRIPT SWITCH CASE语句:当表达式为';ALL';

本地损坏的Java脚本

如果对象中的字段等于某个值,则从数组列表中删除对象

我的NavLink活动类在REACT-ROUTER-V6中出现问题

从客户端更新MongoDB数据库

为什么我的Reaction组件不能使用createBrowserRouter呈现?