amelioration de l'animation

This commit is contained in:
2024-10-08 15:24:45 +00:00
parent 17874f7430
commit 6292b640bd
2 changed files with 131 additions and 113 deletions

View File

@@ -39,10 +39,9 @@
</div> --> </div> -->
<button (click)="setInteractionMode('move')">Déplacer Élément</button> <button (click)="setInteractionMode('move')">Déplacer Élément</button>
<button (click)="setInteractionMode('animate')">Dessiner Vecteur</button> <button (click)="setInteractionMode('animate')">Dessiner Vecteur</button>
<button (click)="setVectorType('linear')">Vecteur Linéaire</button> <button (click)="toggleLoop()">Boucle d'Animation</button>
<button (click)="setVectorType('curved')">Vecteur Courbé</button> <button (click)="replayTimeline()">Play again</button>
<button (click)="toggleLoop()">Activer/Désactiver Boucle d'Animation</button> </div>
</div>
<!-- <canvas #canvas width="800" height="600" <!-- <canvas #canvas width="800" height="600"
(mousedown)="startDrawing($event)" (mousedown)="startDrawing($event)"
@@ -61,6 +60,16 @@
(mousemove)="onCanvasMouseMove($event)" (mousemove)="onCanvasMouseMove($event)"
(mouseup)="onCanvasMouseUp($event)"> (mouseup)="onCanvasMouseUp($event)">
</canvas> </canvas>
<div class="timeline">
<h3>Timeline du joueur sélectionné</h3>
<div *ngIf="selectedPlayer">
<ul>
<li *ngFor="let step of selectedPlayer.timeline; let i = index">
Étape {{ i + 1 }} : Départ ({{ step.startX }}, {{ step.startY }}), Arrivée ({{ step.endX }}, {{ step.endY }}), Durée : {{ step.duration }}
</li>
</ul>
</div>
</div>
<div class="debug"> <div class="debug">
<p>Debug Info: {{ debugInfo }}</p> <p>Debug Info: {{ debugInfo }}</p>
</div> </div>

View File

@@ -2,6 +2,14 @@ import { Component, ViewChild, ElementRef, HostListener } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
interface TimelineStep {
startX: number;
startY: number;
endX: number;
endY: number;
duration: number; // Temps d'animation (en secondes ou ticks)
}
interface Circle { interface Circle {
x: number; x: number;
y: number; y: number;
@@ -18,6 +26,8 @@ interface Circle {
progress: number; // Paramètre de progression sur la courbe progress: number; // Paramètre de progression sur la courbe
startX: number; startX: number;
startY: number; startY: number;
timeline: TimelineStep[]; // Nouvelle propriété pour stocker les étapes d'aniion
currentStepIndex: number; // Suivre l'étape actuelle dans la timeline
} }
interface Rectangle { interface Rectangle {
@@ -41,39 +51,39 @@ interface Rectangle {
export class FootballFieldComponent { export class FootballFieldComponent {
@ViewChild('canvas', { static: true }) canvas!: ElementRef<HTMLCanvasElement>; @ViewChild('canvas', { static: true }) canvas!: ElementRef<HTMLCanvasElement>;
private ctx!: CanvasRenderingContext2D; private ctx!: CanvasRenderingContext2D;
private drawing = false; //private drawing = false;
private lastX = 0; //private lastX = 0;
private lastY = 0; //private lastY = 0;
private startX = 0; private startX = 0;
private startY = 0; private startY = 0;
private draggingPlayer: Circle | null = null; private draggingPlayer: Circle | null = null;
private draggingBall: Circle | null = null; private draggingBall: Circle | null = null;
private draggingPlot: Circle | null = null; //private draggingPlot: Circle | null = null;
private draggingPiquet: Rectangle | null = null; //private draggingPiquet: Rectangle | null = null;
private draggingElement: any = null; private draggingElement: any = null;
private attachedPlayer: Circle | null = null; private attachedPlayer: Circle | null = null;
private magnetRadius: number = 35; // Distance à laquelle le ballon s'attache au joueur 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 attachedToPlayer: boolean = false; // indique si le ballon est attache a un joueur
private offsetX: number = 0; private offsetX: number = 0;
private offsetY: number = 0; private offsetY: number = 0;
private test: boolean = false; private test: boolean = false;
private prevAngle: number = 0; private prevAngle: number = 0;
private isDrawingVector: boolean = false; //private isDrawingVector: boolean = false;
private endX: number = 0; private endX: number = 0;
private endY: number = 0; private endY: number = 0;
private isAnimating: boolean = false; private isAnimating: boolean = false;
private animationSpeed: number = 0.01; private animationSpeed: number = 0.01;
public colors: string[] = ['#ff0000', '#00ff00', //public colors: string[] = ['#ff0000', '#00ff00',
'#0000ff', '#ffff00', // '#0000ff', '#ffff00',
'#ff00ff', '#00ffff', // '#ff00ff', '#00ffff',
'#000000', '#808080', // '#000000', '#808080',
'#ffa500', '#8a2be2']; // '#ffa500', '#8a2be2'];
public selectedColor: string = this.colors[0]; // Couleur sélectionné par défaut (rouge) //public selectedColor: string = this.colors[0]; // Couleur sélectionné par défaut (rouge)
// Options de forme // Options de forme
public selectedShape: string = 'none'; // Par défaut, aucune forme //public selectedShape: string = 'none'; // Par défaut, aucune forme
public playerCount: number = 4; // Nombre de joueurs par défaut public playerCount: number = 8; // Nombre de joueurs par défaut
public plotCount: number = 2; // Nombre de plots par défaut public plotCount: number = 2; // Nombre de plots par défaut
public piquetCount: number = 2; // Nombre de plots par défaut public piquetCount: number = 2; // Nombre de plots par défaut
public ballCount: number = 1; // Nombre de ballons par defaut public ballCount: number = 1; // Nombre de ballons par defaut
@@ -92,10 +102,12 @@ export class FootballFieldComponent {
isMoving:false, isMoving:false,
progress: 0, progress: 0,
startX: 150, startX: 150,
startY: 150 }; startY: 150,
timeline: null,
currentStepIndex: 0 };
public selectedPlayer: Circle | null = null; public selectedPlayer: Circle | null = null;
public isDrawingLine: boolean = false; public isDrawingLine: boolean = false;
public loopAnimation: boolean = true; // Pour répéter l'animation public loopAnimation: boolean = false; // Pour répéter l'animation
public debugInfo: string = ""; // Variable pour les informations de debogage public debugInfo: string = ""; // Variable pour les informations de debogage
public vectorType: 'linear' | 'curved' = 'linear'; // Type de vecteur sélectionné public vectorType: 'linear' | 'curved' = 'linear'; // Type de vecteur sélectionné
public interactionMode: 'move' | 'animate' = 'move'; // Mode d'interaction sélectionné public interactionMode: 'move' | 'animate' = 'move'; // Mode d'interaction sélectionné
@@ -184,78 +196,65 @@ export class FootballFieldComponent {
// Dessiner la ligne si en mode dessin // Dessiner la ligne si en mode dessin
if (this.isDrawingLine && this.selectedPlayer) { if (this.isDrawingLine && this.selectedPlayer) {
console.log("[Draw Field] drawing vector"); //console.log("[Draw Field] drawing vector");
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(this.startX, this.startY); ctx.moveTo(this.startX, this.startY);
ctx.lineTo(this.endX, this.endY); ctx.lineTo(this.endX, this.endY);
ctx.strokeStyle = '#FF0000'; ctx.strokeStyle = '#FF0000';
ctx.lineWidth = 2; ctx.lineWidth = 4;
ctx.stroke(); ctx.stroke();
} }
} }
private animate() { private animate() {
//this.updatePositions(); //this.updatePositions();
if (this.isAnimating && this.selectedPlayer) { //if (this.isAnimating && this.selectedPlayer) {
this.updatePlayerPosition(this.selectedPlayer); // this.updatePlayerPosition(this.selectedPlayer);
//}
if (this.isAnimating) {
// Effacer le canvas pour redessiner
this.ctx.clearRect(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
// Mettre à jour la position de tous les joueurs en suivant leur timeline
this.players.forEach(player => {
if (player.timeline.length > 0) {
this.updatePlayerPosition(player);
}
});
} }
this.drawField(); this.drawField();
requestAnimationFrame(() => this.animate()); requestAnimationFrame(() => this.animate());
} }
private updatePlayerPosition(player: Circle) { private updatePlayerPosition(player: Circle) {
if (player.timeline.length === 0 || player.currentStepIndex >= player.timeline.length) {
return; // Pas d'animation si la timeline est vide
}
const step = player.timeline[player.currentStepIndex];
// Avancer le joueur sur la ligne avec un LERP (Linear Interpolation) // Avancer le joueur sur la ligne avec un LERP (Linear Interpolation)
player.progress += this.animationSpeed; player.progress += this.animationSpeed;
if (player.progress >= 1) { if (player.progress >= 1) {
if (this.loopAnimation) { player.progress = 0; // Réinitialiser la progression
player.progress = 0; // Répéter l'animation player.x = step.endX;
player.x = player.startX; player.y = step.endY;
player.y = player.startY; player.currentStepIndex++; // Passer à l'étape suivante
} else {
this.isAnimating = false; // Si on atteint la fin de la timeline, recommencer ou arrêter
this.selectedPlayer = null; if (player.currentStepIndex >= player.timeline.length) {
if (this.loopAnimation) {
player.currentStepIndex = 0; // Boucle l'animation
} else {
this.isAnimating = false;
}
} }
} else { } else {
const t = player.progress; const t = player.progress;
player.x = (1 - t) * player.startX + t * this.endX; player.x = (1 - t) * step.startX + t * step.endX;
player.y = (1 - t) * player.startY + t * this.endY; player.y = (1 - t) * step.startY + t * step.endY;
} }
} }
// 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 // Mettre à jour debugInfo avec les positions des joueurs et du ballon
private updateDebugInfo() { private updateDebugInfo() {
const playerPositions = this.players.map((player, index) => `Joueur ${index + 1}: (${player.x.toFixed(1)}, ${player.y.toFixed(1)})`).join(', '); const playerPositions = this.players.map((player, index) => `Joueur ${index + 1}: (${player.x.toFixed(1)}, ${player.y.toFixed(1)})`).join(', ');
@@ -281,13 +280,15 @@ export class FootballFieldComponent {
destinationY: null, destinationY: null,
isMoving:false, isMoving:false,
progress: 0, progress: 0,
startX: 150, startX: 0,
startY: 150 }); startY: 0,
timeline: [],
currentStepIndex: 0 });
} }
this.drawField(); this.drawField();
} }
// Créer les cerclesrepresentant les plots // Créer les cercle representant les plots
private createPlots() { private createPlots() {
this.plots = []; this.plots = [];
const radius = 10; // Rayon des cercles const radius = 10; // Rayon des cercles
@@ -304,8 +305,10 @@ export class FootballFieldComponent {
destinationY: null, destinationY: null,
isMoving:false, isMoving:false,
progress: 0, progress: 0,
startX: 150, startX: 0,
startY: 150 }); startY: 0,
timeline: null,
currentStepIndex: 0 });
} }
this.drawField(); this.drawField();
} }
@@ -416,41 +419,27 @@ export class FootballFieldComponent {
ctx.fillText(circle.number.toString(), circle.x, circle.y); ctx.fillText(circle.number.toString(), circle.x, circle.y);
} }
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) { onCanvasMouseDown(event: MouseEvent) {
const { x, y } = this.getMousePos(event); const { x, y } = this.getMousePos(event);
if (this.interactionMode === 'animate') { if (this.interactionMode === 'animate') {
console.log("[Mouse Down] animate elements"); console.log("[Mouse Down] animate elements");
//this.startDrawingVector(x, y); this.selectedPlayer = null;
for (const player of this.players) {
if (this.isInsideCircle(player, x, y)) {
// Sélectionner le joueur
this.selectedPlayer = player;
console.log("[Mouse Down|Animate] selecting player :", this.selectedPlayer);
break;
}
}
if (!this.selectedPlayer) { if (!this.selectedPlayer) {
console.log("[Mouse Down|Animate] select player");
this.selectPlayer(x, y); this.selectPlayer(x, y);
} else if (!this.isDrawingLine) { } else if (!this.isDrawingLine) {
this.startX = this.selectedPlayer.x; this.startX = this.selectedPlayer.x;
this.startY = this.selectedPlayer.y; this.startY = this.selectedPlayer.y;
this.endX = x;
this.endY = y;
this.isDrawingLine = true; this.isDrawingLine = true;
console.log("[Mouse Down|Animate] Draw line - Start:(", console.log("[Mouse Down|Animate] drawing line - Start:(",
this.startX,",", this.startX,",",
this.startY,"), End:(", this.startY,"), End:(",
this.endX,",", this.endX,",",
@@ -466,35 +455,55 @@ export class FootballFieldComponent {
onCanvasMouseMove(event: MouseEvent) { onCanvasMouseMove(event: MouseEvent) {
const { x, y } = this.getMousePos(event); const { x, y } = this.getMousePos(event);
if (this.interactionMode === 'animate') { if (this.interactionMode === 'animate') {
//console.log("[Mouse Move] animate elements"); // Pour déssiner les vecteurs de déplacement des joueurs
this.endX = x;
this.endY = y;
} else if (this.interactionMode === 'move') { } else if (this.interactionMode === 'move') {
this.drag(x, y); this.drag(x, y);
} }
} }
onCanvasMouseUp(event: MouseEvent) { onCanvasMouseUp(event: MouseEvent) {
const { x, y } = this.getMousePos(event); const { x, y } = this.getMousePos(event);
if (this.interactionMode === 'animate') { if (this.interactionMode === 'animate') {
console.log("[Mouse Up] Stop animating elements"); console.log("[Mouse Up] Stop animating elements");
//this.finishDrawingVector(x,y);
if (this.isDrawingLine && this.selectedPlayer) { if (this.isDrawingLine && this.selectedPlayer) {
console.log("[Mouse Up|Animate] Stop drawing vector"); console.log("[Mouse Up|Animate] Stop drawing vector");
this.endX = x; this.endX = x;
this.endY = y; this.endY = y;
this.isDrawingLine = false; this.isDrawingLine = false;
// Ajouter l'étape dans la timeline du joueur
console.log("timeline:", this.selectedPlayer.timeline);
this.selectedPlayer.timeline.push({
startX: this.startX,
startY: this.startY,
endX: this.endX,
endY: this.endY,
duration: 100 // Durée d'animation arbitraire, peut être ajustée
});
this.selectedPlayer.currentStepIndex = 0; // Réinitialiser au début de la timeline
this.startAnimation(); this.startAnimation();
} }
} else if (this.interactionMode === 'move') { } else if (this.interactionMode === 'move') {
console.log("[Mouse Up] Stop moving elements"); console.log("[Mouse Up] Stop moving elements");
this.stopDragging(); this.stopDragging();
// Si on déplace un joueur alors que celui-ci a une animation,
// on supprime toute sa timeline
for (const player of this.players) {
if (this.isInsideCircle(player, x, y)) {
// Sélectionner le joueur
this.selectedPlayer = player;
this.selectedPlayer.timeline = [];
break;
}
}
} }
} }
// Commencer a déplacer un élément du canvas // Commencer a déplacer un élément du canvas
//startDragging(event: MouseEvent) {
startDragging(x:number, y:number) { startDragging(x:number, y:number) {
//const { x, y } = this.getMousePos(event);
if (this.isInsideCircle(this.ball, x, y)) { if (this.isInsideCircle(this.ball, x, y)) {
console.log("inside ball"); console.log("inside ball");
this.ball.isDragging = true; this.ball.isDragging = true;
@@ -523,19 +532,10 @@ export class FootballFieldComponent {
this.offsetY = y - this.draggingPlayer.y; this.offsetY = y - this.draggingPlayer.y;
this.test = true; 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;
// }
} }
// Déplacer le cercle sélectionné // Déplacer l'élément sélectionné sur le canvas
//drag(event: MouseEvent) {
drag(x:number, y:number) { drag(x:number, y:number) {
//const { x, y } = this.getMousePos(event);
if (this.ball.isDragging) { if (this.ball.isDragging) {
console.log("dragging ball"); console.log("dragging ball");
this.ball.x = x - this.offsetX; this.ball.x = x - this.offsetX;
@@ -586,7 +586,7 @@ export class FootballFieldComponent {
if (this.draggingPlayer) { if (this.draggingPlayer) {
this.draggingPlayer.isDragging = false; this.draggingPlayer.isDragging = false;
this.draggingPlayer = null; this.draggingPlayer = null;
this.attachedToPlayer = false; // Détacher le ballon après avoi relache le joueur //this.attachedToPlayer = false; // Détacher le ballon après avoi relache le joueur
} }
if (this.draggingElement) { if (this.draggingElement) {
@@ -864,4 +864,13 @@ export class FootballFieldComponent {
toggleLoop() { toggleLoop() {
this.loopAnimation = !this.loopAnimation; this.loopAnimation = !this.loopAnimation;
} }
replayTimeline() {
// Réinitialiser la progression et l'étape actuelle
this.players.forEach(player => {
player.progress = 0;
player.currentStepIndex = 0;
});
this.isAnimating = true; // Redémarrer l'animation
}
} }