В этом руководстве я поделюсь своим опытом работы с OpenAI GPT-4 для создания интерактивной и увлекательной круговой анимации на основе холста в JavaScript. С помощью GPT-4 мы добавили визуальные эффекты, такие как мигание границ холста и уменьшение размера круга при столкновении с границей. Давайте углубимся в шаги, которые мы предприняли для достижения этого результата.

Шаг 1. Настройте класс Circle с помощью GPT-4. Сначала GPT-4 предоставил мне базовую структуру класса Circle, определяющую свойства и методы, необходимые для наших кругов. Конструктор класса принимает параметры положения (x, y), радиуса, скорости (dx, dy), цвета, скорости роста и силы притяжения.

class Circle {
    constructor(x, y, radius, dx, dy, color, growRate, attractionForce) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.dx = dx;
        this.dy = dy;
        this.color = color;
        this.growRate = 0.05;
        this.attractionForce = 0.0001;
        this.friction = 0.70;
        const arabicLetters = "ABIGAEL";
        this.collisionCounter = 0;
        this.letter = arabicLetters[Math.floor(random(0, arabicLetters.length))];
    }
}

Шаг 2. Внедрите методы рисования и обновления в соответствии с GPT-4. Было предложено добавить в класс Circle два метода: draw() и update(). Метод draw() отвечает за отрисовку круга на холсте, а метод update() обрабатывает движение круга, рост и взаимодействие с другими кругами и границами холста.

draw(ctx) {
        const largestCircle = getLargestCircle();

        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);

        if (this === largestCircle) {
            ctx.fillStyle = 'rgba(40, 300, 255, 0.8)'; // Shiny blue

            // Create a color-changing effect using Math.sin() and Date.now()
            const colorShift = (Math.sin(Date.now() * 0.002) + 1) / 2;
            const red = 255 * colorShift;
            const blue = 255 * (1 - colorShift);
            ctx.strokeStyle = `rgba(${red}, 20, ${blue}, 0.8)`; // Flashing between blue and pink

            ctx.lineWidth = 5;
        } else {
            ctx.fillStyle = this.color;
            ctx.strokeStyle = this.color;
            ctx.lineWidth = 0.5;
        }

        ctx.fill();
        ctx.stroke();
        ctx.closePath();

        // Draw the letter
        ctx.font = `${Math.floor(this.radius * 0.7)}px Arial`;

        // Apply the flashing effect to the letter
        if (this === largestCircle) {
            const colorShift = (Math.sin(Date.now() * 0.002) + 1) / 2;
            const red = 255 * colorShift;
            const blue = 255 * (1 - colorShift);
            ctx.fillStyle = `rgba(${red}, 20, ${blue}, 0.8)`; // Flashing between blue and pink
        } else {
            ctx.fillStyle = 'white';
        }

        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(this.letter, this.x, this.y);
    }

Шаг 3: Уменьшите размер круга при столкновении с границей с помощью наведения GPT-4. С учетом входных данных GPT-4 мы изменили метод update(), чтобы определять, когда круг сталкивается с границей. При столкновении размер круга уменьшается на 10%.

update(canvas, circles) {
            // Collision with borders
            let borderCollision = false;
            if (this.x + this.radius > canvas.width) {
                this.x = canvas.width - this.radius;
                this.dx = -this.dx;
                borderCollision = true;
            } else if (this.x - this.radius < 0) {
                this.x = this.radius;
                this.dx = -this.dx;
                borderCollision = true;
            }
            if (this.y + this.radius > canvas.height) {
                this.y = canvas.height - this.radius;
                this.dy = -this.dy;
                borderCollision = true;
            } else if (this.y - this.radius < 0) {
                this.y = this.radius;
                this.dy = -this.dy;
                borderCollision = true;
            }
            if (borderCollision) {
                this.radius *= 0.9;
            }
        let isConnected = false;
        const largestCircle = getLargestCircle();
        if (this !== largestCircle) {
            const dx = largestCircle.x - this.x;
            const dy = largestCircle.y - this.y;
            const distance = Math.sqrt(dx * dx + dy * dy);
            if (distance > largestCircle.radius + this.radius) {
                const forceX = dx * this.attractionForce * 2;
                const forceY = dy * this.attractionForce * 2;
                this.dx += forceX;
                this.dy += forceY;
            }
        } else {
            // Make the largest circle run away from the rest of the circles
            for (const circle of circles) {
                if (circle === this) continue;
                const dx = this.x - circle.x;
                const dy = this.y - circle.y;
                const distance = Math.sqrt(dx * dx + dy * dy);
                if (distance < this.radius + circle.radius + 100) {
                    const forceX = dx * this.attractionForce * 4;
                    const forceY = dy * this.attractionForce * 4;
                    this.dx += forceX;
                    this.dy += forceY;
                }
            }
        }
        // Collision with other circles
        for (const circle of circles) {
            if (circle === this) continue;
            const dx = this.x - circle.x;
            const dy = this.y - circle.y;
            const distance = Math.sqrt(dx * dx + dy * dy);
            if (distance < this.radius + circle.radius) {
                this.dx = -this.dx;
                this.dy = -this.dy;
                // Reduce circle size by 10%
                this.radius *= 0.99;
                circle.radius *= 0.99;
                // Increment the collisionCounter for both circles
                this.collisionCounter++;
                circle.collisionCounter++;
            }
            // Draw a line between circles when they're close
            if (distance < 100) {
                ctx.beginPath();
                ctx.moveTo(this.x, this.y);
                ctx.lineTo(circle.x, circle.y);
                ctx.strokeStyle = this.color;
                ctx.lineWidth = 0.5;
                ctx.stroke();
                ctx.closePath();
                // Apply attraction force
                const forceX = (circle.x - this.x) * this.attractionForce;
                const forceY = (circle.y - this.y) * this.attractionForce;
                this.dx += forceX;
                this.dy += forceY;
                circle.dx -= forceX;
                circle.dy -= forceY;
                isConnected = true;
            }
        }
        if (!isConnected) {
            this.dx *= this.friction;
            this.dy *= this.friction;
        }
        // Grow the circle slowly
        this.radius += this.growRate;
        this.x += this.dx;
        this.y += this.dy;
    }

 
}

Шаг 4. Заставьте границу холста мигать при изменении самого большого круга GPT-4 рекомендует создать визуальный эффект, при котором граница холста мигает желтым цветом каждый раз, когда самый большой круг перемещается в новый круг. Мы добавили переменную previousLargestCircle для отслеживания предыдущего самого большого круга и обновили ее в функции animate(). Если текущий самый большой круг отличается от предыдущего, мы вызвали функцию flashBorder(), чтобы граница мигала желтым цветом.

let previousLargestCircle = null;
function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for (const circle of circles) {
        circle.draw(ctx);
        circle.update(canvas, circles);
    }
    const currentLargestCircle = getLargestCircle();
    if (previousLargestCircle !== currentLargestCircle) {
        previousLargestCircle = currentLargestCircle;
        flashBorder();
    }
    updateCollisionList(); // Update the list of circles and their collision counts
    requestAnimationFrame(animate);
}
function flashBorder() {
    const borderFlashDuration = 500; // milliseconds
    canvas.style.border = '5px solid yellow';
    setTimeout(() => {
        canvas.style.border = '5px solid green';
    }, borderFlashDuration);
}

Шаг 5: Отобразите список столкновений, вдохновленный GPT-4 GPT-4 предложил отображать список кругов и их соответствующие проценты столкновений, чтобы предоставить пользователям дополнительную информацию. Мы добавили функцию updateCollisionList() для обновления списка кругов и количества их столкновений, а также элемент HTML для отображения этого списка.

function updateCollisionList() {
    const collisionList = document.getElementById("collisionList");
    collisionList.innerHTML = "";
    // Calculate the total number of collisions
    const totalCollisions = circles.reduce((total, circle) => total + circle.collisionCounter, 0);
    for (const circle of circles) {
        const listItem = document.createElement("li");
        // Calculate the percentage of collisions for the current circle
        const collisionPercentage = totalCollisions === 0 ? 0 : ((circle.collisionCounter / totalCollisions) * 100).toFixed(2);
        listItem.textContent = `Circle ${circles.indexOf(circle) + 1}: ${collisionPercentage}%`;
        collisionList.appendChild(listItem);
    }
}

Шаг 6: Я добавил элемент HTML для отображения списка кругов и количества их сговоров в процентах от общего количества всех сговоров. Я также добавил кнопку отдыха, чтобы сбросить анимацию при нажатии.

const circleListContainer = document.createElement("div");
circleListContainer.innerHTML = `
    <h3>Collision List</h3>
    <ul id="collisionList"></ul>
`;
document.body.appendChild(circleListContainer);
function resetAnimation() {
    circles.length = 0;
    createCircles(30);
}
const resetButton = document.getElementById('resetButton');
resetButton.addEventListener('click', resetAnimation);

Наконец, я объявил количество кругов на холсте и доработал код с помощью animate() для запуска анимации.

createCircles(10);
animate();

Я развернул код в Vercel, что было несложно настроить (позже я напишу о своем опыте работы с Vercel).

Вывод: в сотрудничестве с GPT-4 мы успешно создали интерактивную и привлекательную круговую анимацию на основе холста. Усовершенствования включают мигание границ холста при изменении самого большого круга, уменьшение размера круга при столкновении с границей и список, отображающий процент столкновения каждого круга. Эти улучшения делают анимацию более интерактивной и динамичной, обеспечивая пользователям приятный опыт. Помощь GPT-4 в этом проекте была неоценимой, продемонстрировав потенциал ИИ в управлении и улучшении процесса кодирования.

Посетите веб-сайт круговой анимации на основе холста по адресу: https://animation-wheat.vercel.app/