debut d'une timeline
This commit is contained in:
@@ -61,3 +61,32 @@ canvas {
|
||||
.clear-button:hover {
|
||||
background-color: #ff1a1a; /* couleur plus foncée au survol */
|
||||
}
|
||||
|
||||
.timeline-container {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.player-timeline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: #f0f0f0;
|
||||
border: 1px solid #ccc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.timeline-block {
|
||||
position: absolute;
|
||||
height: 30px;
|
||||
background-color: blue;
|
||||
border: 1px solid black;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
@@ -55,11 +55,27 @@
|
||||
(mouseup)="stopDragging()">
|
||||
Votre navigateur ne supporte pas l'élément canvas.
|
||||
</canvas> -->
|
||||
<canvas #canvas width="800" height="600"
|
||||
(mousedown)="onCanvasMouseDown($event)"
|
||||
(mousemove)="onCanvasMouseMove($event)"
|
||||
(mouseup)="onCanvasMouseUp($event)">
|
||||
</canvas>
|
||||
<div class="canvas-and-timeline-container">
|
||||
<canvas #canvas width="800" height="600"
|
||||
(mousedown)="onCanvasMouseDown($event)"
|
||||
(mousemove)="onCanvasMouseMove($event)"
|
||||
(mouseup)="onCanvasMouseUp($event)">
|
||||
</canvas>
|
||||
|
||||
<div class="timeline-container">
|
||||
<div *ngFor="let player of players" class="player-timeline">
|
||||
<span>{{ player.id }}</span>
|
||||
<div class="timeline" (mousedown)="onTimelineMouseDown($event, player)">
|
||||
<div *ngFor="let step of player.timeline; let i = index"
|
||||
class="timeline-block"
|
||||
[style.left]="calculateLeftPosition(step)"
|
||||
[style.width]="calculateBlockWidth(step)"
|
||||
(mousedown)="onTimelineBlockMouseDown($event, player, i)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline">
|
||||
<h3>Timeline du joueur sélectionné</h3>
|
||||
<div *ngIf="selectedPlayer">
|
||||
|
||||
@@ -3,18 +3,21 @@ import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
interface TimelineStep {
|
||||
startX: number;
|
||||
startY: number;
|
||||
endX: number;
|
||||
endY: number;
|
||||
startTime: number; // Le temps de début en millisecondes
|
||||
endTime: number; // Le temps de fin en millisecondes
|
||||
startX: number; // Position de départ de l'élément sur l'axe X
|
||||
startY: number; // Position de départ de l'élément sur l'axe Y
|
||||
endX: number; // Position de fin de l'élément sur l'axe X
|
||||
endY: number; // Position de fin de l'élément sur l'axe Y
|
||||
duration: number; // Temps d'animation (en secondes ou ticks)
|
||||
}
|
||||
|
||||
interface Circle {
|
||||
x: number;
|
||||
y: number;
|
||||
id: number; // Identifiant unique de l'élément
|
||||
x: number; // Position X du centre du cercle de l'élément
|
||||
y: number; // Position Y du centre du cercle de l'élément
|
||||
number: number;
|
||||
radius: number;
|
||||
radius: number; // Rayon du cercle de l'élément
|
||||
isDragging: boolean;
|
||||
vx: number; // vitesse en X
|
||||
vy: number; // vitesse en Y
|
||||
@@ -26,7 +29,7 @@ interface Circle {
|
||||
progress: number; // Paramètre de progression sur la courbe
|
||||
startX: number;
|
||||
startY: number;
|
||||
timeline: TimelineStep[]; // Nouvelle propriété pour stocker les étapes d'aniion
|
||||
timeline: TimelineStep[]; // Stockage des étapes d'animation
|
||||
currentStepIndex: number; // Suivre l'étape actuelle dans la timeline
|
||||
}
|
||||
|
||||
@@ -90,7 +93,8 @@ export class FootballFieldComponent {
|
||||
public players: Circle[] = [];
|
||||
public plots: Circle[] = [];
|
||||
public piquets: Rectangle[] = [];
|
||||
public ball: Circle = { x:400,
|
||||
public ball: Circle = { id: 0,
|
||||
x:400,
|
||||
y:300,
|
||||
number:1,
|
||||
radius:10,
|
||||
@@ -112,6 +116,17 @@ export class FootballFieldComponent {
|
||||
public vectorType: 'linear' | 'curved' = 'linear'; // Type de vecteur sélectionné
|
||||
public interactionMode: 'move' | 'animate' = 'move'; // Mode d'interaction sélectionné
|
||||
|
||||
public isTimelineDragging: boolean = false;
|
||||
public draggedTimelinePlayer: Circle | null = null;
|
||||
public draggedTimelineIndex: number = -1;
|
||||
// La largeur totale de la timeline en pixels.
|
||||
// Cela représente la taille visuelle de la timeline dans laquelle
|
||||
// les différents étapes de mouvement d'un élément sont affichés.
|
||||
public timelineWidth: number = 800;
|
||||
// Décalage horizontal en pixels utilisé lorsque vous commencez à déplacer
|
||||
// un bloc sur la timeline.
|
||||
public dragOffsetX: number = 0;
|
||||
|
||||
ngOnInit() {
|
||||
this.ctx = this.canvas.nativeElement.getContext('2d')!;
|
||||
this.drawField();
|
||||
@@ -233,6 +248,8 @@ export class FootballFieldComponent {
|
||||
const step = player.timeline[player.currentStepIndex];
|
||||
// Avancer le joueur sur la ligne avec un LERP (Linear Interpolation)
|
||||
player.progress += this.animationSpeed;
|
||||
// Temps actuel
|
||||
const currentTime = performance.now();
|
||||
|
||||
if (player.progress >= 1) {
|
||||
player.progress = 0; // Réinitialiser la progression
|
||||
@@ -270,7 +287,8 @@ export class FootballFieldComponent {
|
||||
for (let i = 0; i < this.playerCount; i++) {
|
||||
const x = i * (2 * radius) + radius;
|
||||
const y = radius;
|
||||
this.players.push({ x,
|
||||
this.players.push({ id: i + 1,
|
||||
x,
|
||||
y,
|
||||
number: i + 1,
|
||||
radius,
|
||||
@@ -295,7 +313,8 @@ 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,
|
||||
this.plots.push({ id: i + 1,
|
||||
x,
|
||||
y,
|
||||
number: i + 1,
|
||||
radius,
|
||||
@@ -476,6 +495,8 @@ export class FootballFieldComponent {
|
||||
// Ajouter l'étape dans la timeline du joueur
|
||||
console.log("timeline:", this.selectedPlayer.timeline);
|
||||
this.selectedPlayer.timeline.push({
|
||||
startTime: 0,
|
||||
endTime: 1000,
|
||||
startX: this.startX,
|
||||
startY: this.startY,
|
||||
endX: this.endX,
|
||||
@@ -873,4 +894,96 @@ export class FootballFieldComponent {
|
||||
});
|
||||
this.isAnimating = true; // Redémarrer l'animation
|
||||
}
|
||||
|
||||
/* GESTION DE LA TIMELINE */
|
||||
|
||||
getMousePosOnTimeline(event: MouseEvent): { x: number } {
|
||||
const timelineRect = (event.target as HTMLElement).getBoundingClientRect();
|
||||
return {
|
||||
x: event.clientX - timelineRect.left // Position X par rapport à la timeline
|
||||
};
|
||||
}
|
||||
|
||||
calculateBlockPixelPosition(time: number): number {
|
||||
// La durée totale de la timeline
|
||||
const totalDuration = this.getTotalTimelineDuration();
|
||||
// La largeur de la timeline en pixels (par exemple, 1000px)
|
||||
const timelineWidth = this.timelineWidth;
|
||||
// La durée totale de l'animation en millisecondes
|
||||
return (time / totalDuration) * timelineWidth;
|
||||
}
|
||||
|
||||
onTimelineMouseDown(event: MouseEvent, player: Circle) {
|
||||
// Récupérer la position de la souris lors du clic
|
||||
const { x } = this.getMousePosOnTimeline(event);
|
||||
|
||||
// Chercher si un bloc (une étape du joueur) a été cliqué
|
||||
for (let i = 0; i < player.timeline.length; i++) {
|
||||
const step = player.timeline[i];
|
||||
|
||||
// Calculer la position du bloc en pixels sur la timeline
|
||||
const blockStartX = this.calculateBlockPixelPosition(step.startTime);
|
||||
const blockEndX = this.calculateBlockPixelPosition(step.endTime);
|
||||
|
||||
// Si la souris clique dans un bloc
|
||||
if (x >= blockStartX && x <= blockEndX) {
|
||||
// Début du déplacement du bloc
|
||||
this.isTimelineDragging = true;
|
||||
this.draggedTimelinePlayer = player;
|
||||
this.draggedTimelineIndex = i;
|
||||
// Garder une référence pour un déplacement fluide
|
||||
this.dragOffsetX = x - blockStartX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onTimelineBlockMouseDown(event: MouseEvent, player: Circle, index: number) {
|
||||
this.isTimelineDragging = true;
|
||||
this.draggedTimelinePlayer = player;
|
||||
this.draggedTimelineIndex = index;
|
||||
}
|
||||
|
||||
onMouseMove(event: MouseEvent) {
|
||||
if (this.isTimelineDragging && this.draggedTimelinePlayer) {
|
||||
const { x } = this.getMousePos(event);
|
||||
const totalDuration = this.getTotalTimelineDuration();
|
||||
const newStartTime = (x / this.canvas.nativeElement.width) * totalDuration;
|
||||
|
||||
// Mettre à jour l'étape avec la nouvelle position
|
||||
const step = this.draggedTimelinePlayer.timeline[this.draggedTimelineIndex];
|
||||
step.startTime = Math.max(0, newStartTime); // Ne pas permettre les valeurs négatives
|
||||
|
||||
// Ajuster la position de fin si nécessaire
|
||||
if (this.draggedTimelineIndex > 0) {
|
||||
const previousStep = this.draggedTimelinePlayer.timeline[this.draggedTimelineIndex - 1];
|
||||
previousStep.endTime = step.startTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMouseUp() {
|
||||
if (this.isTimelineDragging) {
|
||||
this.isTimelineDragging = false;
|
||||
this.draggedTimelinePlayer = null;
|
||||
this.draggedTimelineIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
calculateLeftPosition(step: TimelineStep): string {
|
||||
const totalDuration = this.getTotalTimelineDuration();
|
||||
const left = (step.startTime / totalDuration) * 100;
|
||||
return `${left}%`;
|
||||
}
|
||||
|
||||
calculateBlockWidth(step: TimelineStep): string {
|
||||
const totalDuration = this.getTotalTimelineDuration();
|
||||
const width = ((step.endTime - step.startTime) / totalDuration) * 100;
|
||||
return `${width}%`;
|
||||
}
|
||||
|
||||
getTotalTimelineDuration(): number {
|
||||
// Calculer la durée totale de la timeline, par exemple basée sur la durée totale de l'animation
|
||||
return 10000; // Par exemple, 10 000 ms pour une timeline de 10 secondes
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user