From c26b28bddb78136e9d9ed5a8150de67e86bbdd9a Mon Sep 17 00:00:00 2001 From: BENOIT Vincent Date: Fri, 4 Oct 2024 17:11:58 +0200 Subject: [PATCH] on essaye d'animer tout ca --- .../football-field.component.html | 14 +- .../football-field.component.ts | 326 +++++++++++++++--- 2 files changed, 300 insertions(+), 40 deletions(-) diff --git a/src/app/football-field/football-field.component.html b/src/app/football-field/football-field.component.html index f03d45e..2f78742 100644 --- a/src/app/football-field/football-field.component.html +++ b/src/app/football-field/football-field.component.html @@ -37,6 +37,10 @@ --> + + + + - Votre navigateur ne supporte pas l'élément canvas. + --> + +
+

Debug Info: {{ debugInfo }}

+
diff --git a/src/app/football-field/football-field.component.ts b/src/app/football-field/football-field.component.ts index 3eaf532..4fb942f 100644 --- a/src/app/football-field/football-field.component.ts +++ b/src/app/football-field/football-field.component.ts @@ -8,6 +8,14 @@ interface Circle { number: number; radius: number; isDragging: boolean; + vx: number; // vitesse en X + vy: number; // vitesse en Y + destinationX: number | null; + destinationY: number | null; + controlPointX?: number | null; // Pour les vecteurs courbés + controlPointY?: number | null; // Pour les vecteurs courbés + isMoving: boolean; + progress: number; // Paramètre de progression sur la courbe } interface Rectangle { @@ -36,21 +44,19 @@ export class FootballFieldComponent { private lastY = 0; private startX = 0; private startY = 0; - public playerCount: number = 2; // Nombre de joueurs par défaut - public plotCount: number = 1; // Nombre de plots par défaut - public piquetCount: number = 1; // Nombre de plots par défaut - public ballCount: number = 1; // Nombre de ballons par defaut - public players: Circle[] = []; - public plots: Circle[] = []; - public piquets: Rectangle[] = []; - public ball: Circle = { x:400, y:300, number:1, radius:8, isDragging:false }; private draggingPlayer: Circle | null = null; + private draggingBall: Circle | null = null; private draggingPlot: Circle | null = null; private draggingPiquet: Rectangle | null = null; private draggingElement: any = null; + private attachedPlayer: Circle | null = null; + private magnetRadius: number = 35; // Distance à laquelle le ballon s'attache au joueur private attachedToPlayer: boolean = false; // indique si le ballon est attache a un joueur private offsetX: number = 0; private offsetY: number = 0; + private test: boolean = false; + private prevAngle: number = 0; + private isDrawingVector: boolean = false; public colors: string[] = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', @@ -60,6 +66,27 @@ export class FootballFieldComponent { public selectedColor: string = this.colors[0]; // Couleur sélectionné par défaut (rouge) // Options de forme public selectedShape: string = 'none'; // Par défaut, aucune forme + public playerCount: number = 2; // Nombre de joueurs par défaut + public plotCount: number = 1; // Nombre de plots par défaut + public piquetCount: number = 1; // Nombre de plots par défaut + public ballCount: number = 1; // Nombre de ballons par defaut + public players: Circle[] = []; + public plots: Circle[] = []; + public piquets: Rectangle[] = []; + public ball: Circle = { x:400, + y:300, + number:1, + radius:10, + isDragging:false, + vx:0, + vy:0, + destinationX: null, + destinationY: null, + isMoving:false, + progress: 0 }; + public debugInfo: string = ""; // Variable pour les informations de debogage + public vectorType: 'linear' | 'curved' = 'linear'; // Type de vecteur sélectionné + public interactionMode: 'move' | 'animate' = 'move'; // Mode d'interaction sélectionné ngOnInit() { this.ctx = this.canvas.nativeElement.getContext('2d')!; @@ -67,6 +94,7 @@ export class FootballFieldComponent { this.createPlayers(); this.createPlots(); this.createPiquets(); + this.animate(); // Lancer l'animation } // Dessine le terrain de football @@ -140,6 +168,54 @@ export class FootballFieldComponent { this.drawPlots(); this.drawPiquets(); this.drawBall(); + this.updateDebugInfo(); + } + + private animate() { + this.updatePositions(); + this.drawField(); + requestAnimationFrame(() => this.animate()); + } + + private updatePositions() { + // Mettre à jour les positions des joueurs + this.players.forEach(player => { + player.x += player.vx; + player.y += player.vy; + + // Collision avec les bords + if (player.x - player.radius < 0 || player.x + player.radius > this.canvas.nativeElement.width) { + player.vx = -player.vx; + } + if (player.y - player.radius < 0 || player.y + player.radius > this.canvas.nativeElement.height) { + player.vy = -player.vy; + } + }); + + // Si le ballon est attaché à un joueur, le suivre + if (this.attachedPlayer) { + this.updateBallPositionOnPlayer(); + } else { + // Mise à jour de la position du ballon + this.ball.x += this.ball.vx; + this.ball.y += this.ball.vy; + + // Collision avec les bords pour le ballon + if (this.ball.x - this.ball.radius < 0 || this.ball.x + this.ball.radius > this.canvas.nativeElement.width) { + this.ball.vx = -this.ball.vx; + } + if (this.ball.y - this.ball.radius < 0 || this.ball.y + this.ball.radius > this.canvas.nativeElement.height) { + this.ball.vy = -this.ball.vy; + } + } + } + + // Mettre à jour debugInfo avec les positions des joueurs et du ballon + private updateDebugInfo() { + const playerPositions = this.players.map((player, index) => `Joueur ${index + 1}: (${player.x.toFixed(1)}, ${player.y.toFixed(1)})`).join(', '); + const ballPosition = `Ballon: (${this.ball.x.toFixed(1)}, ${this.ball.y.toFixed(1)})`; + + this.debugInfo = `${playerPositions}, ${ballPosition}`; } // Créer les cercles nuerotes representant les joueurs @@ -149,7 +225,16 @@ export class FootballFieldComponent { for (let i = 0; i < this.playerCount; i++) { const x = i * (2 * radius) + radius; const y = radius; - this.players.push({ x, y, number: i + 1, radius, isDragging: false }); + this.players.push({ x, + y, + number: i + 1, + radius, + isDragging: false, + vx:0, vy:0, + destinationX: null, + destinationY: null, + isMoving:false, + progress: 0 }); } this.drawField(); } @@ -161,7 +246,16 @@ export class FootballFieldComponent { for (let i = 0; i < this.plotCount; i++) { const x = (this.canvas.nativeElement.width - radius) - (i) * (2 * radius); const y = radius; - this.plots.push({ x, y, number: i + 1, radius, isDragging: false }); + this.plots.push({ x, + y, + number: i + 1, + radius, + isDragging: false, + vx: 0, vy: 0, + destinationX: null, + destinationY: null, + isMoving:false, + progress: 0 }); } this.drawField(); } @@ -170,10 +264,10 @@ export class FootballFieldComponent { private createPiquets() { this.piquets = []; for (let i = 0; i < this.piquetCount; i++) { - const x = 300; - const y = 400; - const width = 7; - const height = 70; + const width = 8; + const height = 100; + const x = (i * width); + const y = (this.canvas.nativeElement.height - height); this.piquets.push({ x, y, width, height, isDragging: false }); } this.drawField(); @@ -272,32 +366,81 @@ export class FootballFieldComponent { ctx.fillText(circle.number.toString(), circle.x, circle.y); } - // Commencer a déplacer un cercle + private drawVectors() { + const ctx = this.ctx; + this.players.forEach(player => { + ctx.beginPath(); + ctx.moveTo(player.x, player.y); + ctx.lineTo(player.x + player.vx * 20, player.y + player.vy * 20); // Longueur du vecteur + ctx.strokeStyle = '#FF0000'; + ctx.lineWidth = 2; + ctx.stroke(); + }); + + // Dessiner le vecteur de mouvement du ballon + ctx.beginPath(); + ctx.moveTo(this.ball.x, this.ball.y); + ctx.lineTo(this.ball.x + this.ball.vx * 20, this.ball.y + this.ball.vy * 20); + ctx.strokeStyle = '#FFA500'; + ctx.lineWidth = 2; + ctx.stroke(); + } + + onCanvasMouseDown(event: MouseEvent) { + const { x, y } = this.getMousePos(event); + if (this.interactionMode === 'animate') { + console.log("plop"); + } else if (this.interactionMode === 'move') { + console.log("move elements"); + this.startDragging(event); + } + } + + onCanvasMouseMove(event: MouseEvent) { + const { x, y } = this.getMousePos(event); + } + + onCanvasMouseUp(event: MouseEvent) { + const { x, y } = this.getMousePos(event); + } + + // Commencer a déplacer un élément du canvas startDragging(event: MouseEvent) { const { x, y } = this.getMousePos(event); + + if (this.isInsideCircle(this.ball, x, y)) { + console.log("inside ball"); + this.ball.isDragging = true; + this.attachedPlayer = null; // Libérer le ballon lors du clic + this.offsetX = x - this.ball.x; + this.offsetY = y - this.ball.y; + return; + } + this.draggingElement = this.plots.find(plot => this.isInsideCircle(plot, x, y)) || this.piquets.find(piquet => this.isInsideRectangle(piquet, x, y)) || (this.isInsideCircle(this.ball, x, y) ? this.ball : null); - this.draggingPlayer = this.players.find(circle => { - return this.isInsideCircle(circle, x, y); - }); - + if (this.draggingElement) { this.draggingElement.isDragging = true; this.offsetX = x - this.draggingElement.x; this.offsetY = y - this.draggingElement.y; } + this.draggingPlayer = this.players.find(player => { + return this.isInsideCircle(player, x, y); + }); if (this.draggingPlayer) { this.draggingPlayer.isDragging = true; this.offsetX = x - this.draggingPlayer.x; this.offsetY = y - this.draggingPlayer.y; + this.test = true; } - // Vérifier si le joueur touche le ballon - if (this.draggingPlayer && this.isInsideCircle(this.ball, this.draggingPlayer.x, this.draggingPlayer.y)) { - this.attachedToPlayer = true; - } +// // Vérifier si le joueur touche le ballon +// if (this.draggingPlayer && this.isInsideCircle(this.ball, this.draggingPlayer.x, this.draggingPlayer.y)) { +// this.attachedToPlayer = true; +// } } @@ -305,15 +448,29 @@ export class FootballFieldComponent { drag(event: MouseEvent) { const { x, y } = this.getMousePos(event); + if (this.ball.isDragging) { + this.ball.x = x - this.offsetX; + this.ball.y = y - this.offsetY; + this.drawField(); + return; + } + if (this.draggingPlayer && this.draggingPlayer.isDragging) { this.draggingPlayer.x = x - this.offsetX; this.draggingPlayer.y = y - this.offsetY; - // Si le ballon est attaché au joueur deplacer le ballon avec lui en calculant - // la tangente - if (this.attachedToPlayer) { - this.updateBallPositionOnTangent(); + // Attacher le ballon si un joueur se rapproche suffisamment + const nearestPlayer = this.getNearestPlayerToBall(); + if (nearestPlayer && this.getDistance(this.ball, nearestPlayer) < this.magnetRadius) { + this.attachedPlayer = nearestPlayer; + this.updateBallPositionOnPlayer(); } + +// // Si le ballon est attaché au joueur deplacer le ballon avec lui en calculant +// // la tangente +// if (this.attachedToPlayer) { +// this.updateBallPositionOnTangent(); +// } this.drawField(); return; } @@ -327,12 +484,18 @@ export class FootballFieldComponent { } - // Arrêter de déplacer le cercle + // Arrêter de déplacer stopDragging() { + if (this.ball.isDragging) { + this.ball.isDragging = false; + this.test = false; + this.prevAngle = 0; + } + if (this.draggingPlayer) { this.draggingPlayer.isDragging = false; this.draggingPlayer = null; - this.attachedToPlayer = false; // Détacher le ballon après avoirelache le joueur + this.attachedToPlayer = false; // Détacher le ballon après avoi relache le joueur } if (this.draggingElement) { @@ -341,19 +504,93 @@ export class FootballFieldComponent { } } - // Calculer la nouvelle position du ballon a la tangente du joueur - private updateBallPositionOnTangent() { - const player = this.draggingPlayer; + private moveAlongLine(player: Circle) { + const dx = player.destinationX! - player.x; + const dy = player.destinationY! - player.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < 1) { + player.x = player.destinationX!; + player.y = player.destinationY!; + player.isMoving = false; + } else { + player.vx = (dx / distance) * 2; + player.vy = (dy / distance) * 2; + player.x += player.vx; + player.y += player.vy; + } + } + + private moveAlongCurve(player: Circle) { + player.progress += 0.01; + if (player.progress >= 1) { + player.x = player.destinationX!; + player.y = player.destinationY!; + player.isMoving = false; + player.progress = 0; + } else { + const t = player.progress; + const x = (1 - t) * (1 - t) * player.x + + 2 * (1 - t) * t * player.controlPointX! + + t * t * player.destinationX!; + const y = (1 - t) * (1 - t) * player.y + + 2 * (1 - t) * t * player.controlPointY! + + t * t * player.destinationY!; + player.x = x; + player.y = y; + } + } + + private updateBallPositionOnPlayer() { + const player = this.attachedPlayer; if (!player) return; - const dx = this.ball.x - this.draggingPlayer.x; - const dy = this.ball.y - this.draggingPlayer.y; - const distance = Math.sqrt(dx * dy + dy * dy); // Distance entre le joueur et le ballon - const angle = Math.atan2(dy, dx); // Calculer l'angle entre le joueur et le ballon + const dx = this.ball.x - player.x; + const dy = this.ball.y - player.y; + let angle = 0; + if (this.test) { + angle = Math.atan2(dy, dx); + this.prevAngle = angle; + this.test = false; + } else { + angle = this.prevAngle; + } - // Positionner le ballon a la tangente du joueur - this.ball.x = player.x + Math.cos(angle) * (player.radius + this.ball.radius); - this.ball.y = player.y + Math.sin(angle) * (player.radius + this.ball.radius); + this.ball.x = player.x + (player.radius + this.ball.radius) * Math.cos(angle); + this.ball.y = player.y + (player.radius + this.ball.radius) * Math.sin(angle); + } + + // Calculer la nouvelle position du ballon a la tangente du joueur +// private updateBallPositionOnTangent() { +// const player = this.draggingPlayer; +// if (!player) return; +// +// const dx = this.ball.x - player.x; +// const dy = this.ball.y - player.y; +// const distance = Math.sqrt(dx * dy + dy * dy); // Distance entre le joueur et le ballon +// const angle = Math.atan2(dy, dx); // Calculer l'angle entre le joueur et le ballon +// +// // Positionner le ballon a la tangente du joueur +// this.ball.x = player.x + Math.cos(angle) * (player.radius + this.ball.radius); +// this.ball.y = player.y + Math.sin(angle) * (player.radius + this.ball.radius); +// } + + private getNearestPlayerToBall(): Circle | null { + let nearestPlayer: Circle | null = null; + let minDistance = this.magnetRadius; + + this.players.forEach(player => { + const distance = this.getDistance(this.ball, player); + if (distance < minDistance) { + minDistance = distance; + nearestPlayer = player; + } + }); + return nearestPlayer; + } + + private getDistance(circle1: Circle, circle2: Circle): number { + return Math.sqrt((circle1.x - circle2.x) ** 2 + (circle1.y - circle2.y) ** 2); } // Vérifier si la souris et a l'intérieur d'un cercle @@ -462,4 +699,15 @@ export class FootballFieldComponent { this.drawField(); // Redessine le terrain de football this.updatePlayers(); } + + // Méthode pour changer le mode d'interaction (déplacer ou dessiner un vecteur) + setInteractionMode(mode: 'move' | 'animate') { + this.interactionMode = mode; + this.isDrawingVector = false; + //this.selectedPlayer = null; // Réinitialiser la sélection de joueur pour éviter tout déplacement involontaire + } + // Méthode pour définir le type de vecteur (linéaire ou courbé) + setVectorType(type: 'linear' | 'curved') { + this.vectorType = type; + } }