Bonjour aux Alsanautes, je vous soumets ceci qui m'amuse beaucoup : comment transcoder le code suivant d'un canvas 2D en pur javascript sans canvas, c-à-d que l'on puisse articuler par css une class balloon aisément.

Le projet ne se limite pas au code qui suit parce que j'envisage d'écrire un clic pour une explosion des ballons avec un transfert de force en proximité, transform scale(1.5), filter:blur(_px) et transition opacity ... avec random et setInterval pour ne conserver que le dernier avec une annonce : - "le ballon gagnant est _".

Le code html qui suit est fonctionnel, c-à-d qu'il fonctionne bien (à vous de le constater et de le confirmer).

Ce code contient html, css et js :
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Animation des Ballons</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
position:relative;
margin:auto;
top:50px;
      	background-image: linear-gradient(to bottom, #5f87ff, #959f93);
        }

#startButton{
z-index:2;
position:absolute;
display:inline-block;
margin:20px 50%;
width:180px;
padding:10px;
left:-100px;
box-shadow:6px 6px 9px rgba(0,0,0,0.4)}

#startButton:active{box-shadow:none}

    </style>
</head>
<body>
    <button id="startButton">Démarrer l'animation</button>
    <canvas id="canvas"></canvas>
    <script>
        const canvas = document.getElementById("canvas");
        const ctx = canvas.getContext("2d");

        canvas.width = window.innerWidth - 100;
        canvas.height = window.innerHeight - 100;

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

        // Constantes
        const gravity = 0.99; // Gravité
        const friction = 0.95; // Frottement
        const maxSpeed = 5; // Vitesse maximale
        const balloonCount = 12; // Nombre de ballons***au choix*****************************************
        const restingThreshold = 0.1; // Seuil pour considérer un ballon comme au repos
        const overlapThreshold = 2 * 75; // Seuil de chevauchement entre ballons (diamètre)

// Classe pour un ballon
class Balloon {
    constructor(number, x, y, radius) {
        this.number = number;
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.dx = (Math.random() + 4.5) * 5; // Vitesse horizontale
	this.dy = Math.random() * maxSpeed * 2 - 3; // Vitesse verticale plus forte pour donner un angle de chute*/
        this.color = `hsl(${Math.random() * 360}, 70%, 80%)`; // Couleur pastel aléatoire
        this.rotation = Math.random() * Math.PI * 2; // Rotation aléatoire
        this.rotationSpeed = Math.random() * 0.7 - 0.05; // Vitesse de rotation
        this.isResting = false; // État du ballon (en mouvement ou au repos)
    }

    // Dessiner le ballon avec ombre interne
    draw() {
        ctx.save();
        ctx.translate(this.x, this.y);
        ctx.rotate(this.rotation);

        // Dessiner le ballon
        ctx.beginPath();
        ctx.arc(0, 0, this.radius, 0, Math.PI * 2);
        ctx.fillStyle = this.color;
        ctx.fill();

        // Dessiner l'ombre interne
        const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, this.radius);
        gradient.addColorStop(0, 'rgba(0, 0, 0, 0.4)'); // Centre transparent 0.4
        gradient.addColorStop(1, 'rgba(0, 0, 0, 0.8)'); // Bord sombre
        ctx.fillStyle = gradient;
        ctx.beginPath();
        ctx.arc(0, 0, this.radius, 0, Math.PI * 2);
        ctx.fill();

        // Dessiner le numéro
        ctx.fillStyle = '#fff';
        ctx.font = `${this.radius / 1.2}px Arial`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(this.number, 0, 0);

        ctx.restore();
    }

    // Mise à jour de la position et de la vitesse
    update(balloons) {
        // Appliquer la gravité
        this.dy += gravity;

        // Arrêter les ballons au repos
        if (Math.abs(this.dy) < restingThreshold && this.y + this.radius >= canvas.height) {
            this.dy = 0;
            this.dx *= 0.95; // Réduction de la vitesse horizontale
            this.isResting = true;
        } else {
            this.isResting = false;
        }

        // Déplacement
        this.x += this.dx;
        this.y += this.dy;

        // Rotation en fonction de la vitesse horizontale (sans facteur de division incorrect)
        this.rotationSpeed = this.dx / this.radius;  // Utilisation directe de dx pour la rotation
        this.rotation += this.rotationSpeed; // Mise à jour de la rotation

        // Collision avec le sol
        if (this.y + this.radius > canvas.height) {
            this.y = canvas.height - this.radius;
            this.dy *= -friction; // Rebondir avec un peu de friction ****************************
            this.rotationSpeed *= friction; // Réduire la vitesse de rotation lors du rebond
        }

        // Collision avec les murs latéraux
        if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
            this.dx *= -friction * 2 / 3; // Petite modif subtile
            if (this.x + this.radius > canvas.width) this.x = canvas.width - this.radius;
            if (this.x - this.radius < 0) this.x = this.radius;
        }

        // Collision avec d'autres ballons
        for (let i = 0; i < balloons.length; i++) {
            if (this === balloons[i]) continue;

            const dist = Math.hypot(this.x - balloons[i].x, this.y - balloons[i].y);
            if (dist - this.radius * 2 < 0) {
                resolveCollision(this, balloons[i]);
            }
        }

        this.draw();
    }
}


        // Fonction pour résoudre la collision entre deux ballons
        function resolveCollision(balloon1, balloon2) {
            const dist = Math.hypot(balloon1.x - balloon2.x, balloon1.y - balloon2.y);
            const overlap = balloon1.radius * 2 - dist;

            if (overlap > 0) {
                const angle = Math.atan2(balloon1.y - balloon2.y, balloon1.x - balloon2.x);
                const normalX = Math.cos(angle);
                const normalY = Math.sin(angle);

                // Calculer la vitesse relative entre les deux ballons
                const relativeVelocityX = balloon1.dx - balloon2.dx;
                const relativeVelocityY = balloon1.dy - balloon2.dy;

                // Calculer la vitesse le long de la normale de la collision
                const velocityAlongNormal = relativeVelocityX * normalX + relativeVelocityY * normalY;

                // Calculer la restitution (rebond) de la collision
                if (velocityAlongNormal > 0) return;

                const restitution = 1; // Coefficient de restitution (rebond parfait)
                const impulse = (2 * velocityAlongNormal) / (balloon1.radius + balloon2.radius);

                // Appliquer l'impulsion pour ajuster les vitesses des ballons
                balloon1.dx -= impulse * balloon2.radius * normalX;
                balloon1.dy -= impulse * balloon2.radius * normalY;
                balloon2.dx += impulse * balloon1.radius * normalX;
                balloon2.dy += impulse * balloon1.radius * normalY;

                // Appliquer la rotation après la collision
                const rotationChange1 = overlap * impulse * normalX;
                const rotationChange2 = overlap * impulse * normalX;
                balloon1.rotationSpeed += rotationChange1 / balloon1.radius;
                balloon2.rotationSpeed += rotationChange2 / balloon2.radius;

                // Déplacer les ballons pour éviter la superposition
                const moveDistance = overlap / 2;
                balloon1.x -= moveDistance * normalX;
                balloon1.y -= moveDistance * normalY;
                balloon2.x += moveDistance * normalX;
                balloon2.y += moveDistance * normalY;
            }
        }

        // Fonction pour empêcher la superposition des ballons
        function preventOverlap(balloons) {
            for (let i = 0; i < balloons.length; i++) {
                for (let j = i + 1; j < balloons.length; j++) {
                    const dist = Math.hypot(balloons[i].x - balloons[j].x, balloons[i].y - balloons[j].y);
                    if (dist < 2 * balloons[i].radius) {
                        // Si les ballons sont trop proches, les séparer
                        const angle = Math.atan2(balloons[i].y - balloons[j].y, balloons[i].x - balloons[j].x);
                        const overlap = 2 * balloons[i].radius - dist;
                        const moveX = Math.cos(angle) * overlap / 2;
                        const moveY = Math.sin(angle) * overlap / 2;

                        // Déplacer les ballons pour respecter la distance minimale
                        balloons[i].x += moveX;
                        balloons[i].y += moveY;
                        balloons[j].x -= moveX;
                        balloons[j].y -= moveY;
                    }
                }
            }
        }

        // Initialiser les ballons
        let balloons = [];
        for (let i = 0; i < balloonCount; i++) {
            let balloon = new Balloon(i + 1, Math.random() * (canvas.width - 150) + 75, Math.random() * (canvas.height - 150) + 75, 75);
            balloons.push(balloon);
        }

        // Animer les ballons
        function animate() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            preventOverlap(balloons);

            for (let balloon of balloons) {
                balloon.update(balloons);
            }

            requestAnimationFrame(animate);
        }

        // Démarrer l'animation au clic sur le bouton
        document.getElementById("startButton").addEventListener("click", () => {
            animate();
        });

    </script>
</body>
</html>

Modifié par Gout-de-l-esprit (07 Dec 2024 - 08:06)
Il s'agit dans ce contexte où les écritures canvas sont moins accessibles que le pur js, donc qu'il faille le transcoder et ensuite le modifier en pur js.

Une proposition que l'AI de ChatGpt puisse comprendre ?
Modifié par Gout-de-l-esprit (07 Dec 2024 - 08:28)
J'ai longuement conversé avec ChatGpt pour obtenir ce résultat (mes prompts), or cette AI ne cesse de se mélanger les pinceaux, hi !

Et selon mes prompts, l'AI se refuse à comprendre la totalité des Lois Physiques Des Corps Exposés A La Gravité En Situation Terrestre. Notamment en déperdition d'énergie récurrente ... de proche en proche.

Mais peut-être m'y suis-je mal exprimé ?

Une autre stratégie ?
Modifié par Gout-de-l-esprit (07 Dec 2024 - 08:26)
salut
Il faut utiliser le nouveau moteur anti-gravité disponible sur le site de ZalTech.
Avec la fluctuation quantique des oscillateurs harmoniques bien réglée, çà devrait le faire.
Je crois que la formule en dit assez:
upload/1733606296-47649-grav.png
Modérateur
drphilgood a écrit :
salut
Il faut utiliser le nouveau moteur anti-gravité disponible sur le site de ZalTech.
Avec la fluctuation quantique des oscillateurs harmoniques bien réglée, çà devrait le faire.
Je crois que la formule en dit assez:
upload/1733606296-47649-grav.png


Salut,

Autant faire du web assembly pour récupérer un moteur physique Smiley cligne
Merci à DrPhilgood et à Niuxe d'avoir - éventuellement - expérimenté mon code html css js.

Or, ce code fonctionne bien ! sauf de présenter quelque fébrilité que je ne parviens à résoudre.

Cela ne suffit évidemment pas d'évoquer du web assembly pour résoudre ma question.

Ainsi, avant de me railler, daignez constater par un copier-coller ce que cela révèle en votre nav web.

D'ores et déjà le résultat est bluffant tel qu'il m'avait bluffé : des balles rebondissantes et tournantes que l'on n'avait pas trouvées ailleurs ... sauf ma pauvre connaissance.