diff --git a/package-lock.json b/package-lock.json index 67ab09a..6fdb1a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@angular/animations": "^18.2.0", + "@angular/cdk": "^18.2.6", "@angular/common": "^18.2.0", "@angular/compiler": "^18.2.0", "@angular/core": "^18.2.0", @@ -351,6 +352,23 @@ } } }, + "node_modules/@angular/cdk": { + "version": "18.2.6", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.6.tgz", + "integrity": "sha512-Gfq/iv4zhlKYpdQkDaBRwxI71NHNUHM1Cs1XhnZ0/oFct5HXvSv1RHRGTKqBJLLACaAPzZKXJ/UglLoyO5CNiQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/cli": { "version": "18.2.6", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.6.tgz", @@ -6601,7 +6619,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -10772,7 +10790,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "entities": "^4.4.0" diff --git a/package.json b/package.json index 0e6f455..b2409a3 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "football-drawing", - "version": "0.0.0", + "version": "0.1.0", "scripts": { "ng": "ng", - "start": "ng serve", + "start": "ng serve --host 0.0.0.0 --disable-host-check", "build": "ng build", "watch": "ng build --watch --configuration development", "test": "ng test" @@ -11,6 +11,7 @@ "private": true, "dependencies": { "@angular/animations": "^18.2.0", + "@angular/cdk": "^18.2.6", "@angular/common": "^18.2.0", "@angular/compiler": "^18.2.0", "@angular/core": "^18.2.0", diff --git a/src/app/app.component.html b/src/app/app.component.html index 36093e1..a64bc90 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,336 +1,2 @@ - - - - - - - - - - - -
-
-
- -

Hello, {{ title }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
-
-
- - - - - - - - - - - + + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e90a6bf..2ea4a9d 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,10 +1,11 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { FootballFieldComponent } from './football-field/football-field.component'; @Component({ selector: 'app-root', standalone: true, - imports: [RouterOutlet], + imports: [RouterOutlet, FootballFieldComponent], templateUrl: './app.component.html', styleUrl: './app.component.css' }) diff --git a/src/app/app.module.ts b/src/app/app.module.ts new file mode 100644 index 0000000..3d42796 --- /dev/null +++ b/src/app/app.module.ts @@ -0,0 +1,9 @@ +import { NgModule } from '@angular/core'; +import { FootballFieldComponent } from './football-field/football-field.component'; + +@NgModule({ + declarations: [ + FootballFieldComponent + ], +}) +export class AppModule {} diff --git a/src/app/football-field/football-field.component.css b/src/app/football-field/football-field.component.css new file mode 100644 index 0000000..12c7f85 --- /dev/null +++ b/src/app/football-field/football-field.component.css @@ -0,0 +1,63 @@ +.football-container { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 20px; +} + +.toolbar { + display: flex; + justify-content: space-between; + align-items: center; + width: 620px; /* largeur égale au canvas pour aligner */ + margin-bottom: 10px; +} + +canvas { + border: 2px solid #000000; /* Bordure noire pour le canvas */ + margin-top: 10px; +} + +.color-palette { + display: flex; + justify-content: center; + margin-bottom: 10px; +} + +.shape-selector { + display: flex; + align-items: center; + margin-right: 10px; +} + +.color-box { + width: 30px; + height: 30px; + margin: 0 5px; + border-radius: 50%; + border: 2px solid transparent; + cursor: pointer; + transition: border 0.2s; +} + +.color-box.selected { + border: 2px solid #000000; +} + +.controls { + margin-top: 15px; +} + +.clear-button { + padding: 10px 20px; + background-color: #ff4d4d; /* Rouge vif */ + color: white; + border: none; + cursor: pointer; + font-size: 16px; + border-radius: 5px; +} + +.clear-button:hover { + background-color: #ff1a1a; /* couleur plus foncée au survol */ +} diff --git a/src/app/football-field/football-field.component.html b/src/app/football-field/football-field.component.html new file mode 100644 index 0000000..bca802b --- /dev/null +++ b/src/app/football-field/football-field.component.html @@ -0,0 +1,33 @@ +
+ +
+
+
+
+
+ + + + +
+ + +
+
+ + + Your browser does not support the canvas element. + + +
+ diff --git a/src/app/football-field/football-field.component.spec.ts b/src/app/football-field/football-field.component.spec.ts new file mode 100644 index 0000000..7b49806 --- /dev/null +++ b/src/app/football-field/football-field.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FootballFieldComponent } from './football-field.component'; + +describe('FootballFieldComponent', () => { + let component: FootballFieldComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FootballFieldComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FootballFieldComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/football-field/football-field.component.ts b/src/app/football-field/football-field.component.ts new file mode 100644 index 0000000..446b7a8 --- /dev/null +++ b/src/app/football-field/football-field.component.ts @@ -0,0 +1,213 @@ +import { Component, ViewChild, ElementRef, HostListener } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-football-field', + standalone: true, + imports: [ + CommonModule, + FormsModule + ], + templateUrl: './football-field.component.html', + styleUrl: './football-field.component.css' +}) +export class FootballFieldComponent { + @ViewChild('canvas', { static: true }) canvas!: ElementRef; + private ctx!: CanvasRenderingContext2D; + private drawing = false; + private lastX = 0; + private lastY = 0; + private startX = 0; + private startY = 0; + + public colors: string[] = ['#ff0000', '#00ff00', + '#0000ff', '#ffff00', + '#ff00ff', '#00ffff', + '#000000', '#808080', + '#ffa500', '#8a2be2']; + 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 + + ngOnInit() { + this.ctx = this.canvas.nativeElement.getContext('2d')!; + this.drawField(); + } + + // Dessine le terrain de football + private drawField() { + const ctx = this.ctx; + const canvas = this.canvas.nativeElement; + + // Efface le canvas + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Dessine le terrain vert + ctx.fillStyle = '#3ba047'; // Vert pelouse + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Bordures blanches du terrain + ctx.strokeStyle = '#ffffff'; // Blanc + ctx.lineWidth = 3; + + // Ligne de touche extĂ©rieure + ctx.strokeRect(10, 10, canvas.width - 20, canvas.height - 20); + + // Ligne mĂ©diane + ctx.beginPath(); + ctx.moveTo(canvas.width / 2, 10); + ctx.lineTo(canvas.width / 2, canvas.height - 10); + ctx.stroke(); + + // Cercle central + ctx.beginPath(); + ctx.arc(canvas.width / 2, canvas.height / 2, 70, 0, Math.PI * 2); + ctx.stroke(); + + // Point central + ctx.fillStyle = '#ffffff'; // Blanc + ctx.beginPath(); + ctx.arc(canvas.width / 2, canvas.height / 2, 5, 0, Math.PI * 2); + ctx.fill(); + + // Points de penalty + ctx.beginPath(); + ctx.arc(90, canvas.height / 2, 3, 0, Math.PI * 2); // Point gauche + ctx.fill(); + ctx.beginPath(); + ctx.arc(canvas.width - 90, canvas.height / 2, 3, 0, Math.PI * 2); // Point droit + ctx.fill(); + + // Surfaces de rĂ©paration + ctx.strokeRect(10, canvas.height / 2 - 150, 120, 300); // Surface gauche + ctx.strokeRect(canvas.width - 130, canvas.height / 2 - 150, 120, 300); // Surface droite + + // Surface de but + ctx.strokeRect(10, canvas.height / 2 - 75, 50, 150); // Surface gauche + ctx.strokeRect(canvas.width - 60, canvas.height / 2 - 75, 50, 150); // Surface droite + + // Corners + ctx.beginPath(); + ctx.arc(10, 10, 20, 0, Math.PI / 2); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(canvas.width - 10, 10 , 20, Math.PI / 2, Math.PI ); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(canvas.width - 10, canvas.height - 10, 20, Math.PI, -Math.PI /2 ); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(10, canvas.height - 10 , 20, Math.PI * 3/2, Math.PI * 2 ); + ctx.stroke(); + } + + // RĂ©initialise le dessin sans effacer le terrain + clearCanvas() { + this.drawField(); + } + + // Change la couleur sĂ©lectionnĂ©e + setColor(color: string) { + this.selectedColor = color; + } + + // Commence Ă  dessiner quand la souris est enfoncĂ©e + startDrawing(event: MouseEvent) { + this.drawing = true; + const { x, y } = this.getMousePos(event); + this.startX = x; + this.startY = y; + this.lastX = x; + this.lastY = y; + } + +// // Dessine en suivant la souris +// draw(event: MouseEvent) { +// if (!this.drawing) return; +// +// const { x, y } = this.getMousePos(event); +// this.ctx.strokeStyle = this.selectedColor; // Utilise la couleur selectionnĂ©e +// this.ctx.lineWidth = 2; +// this.ctx.beginPath(); +// this.ctx.moveTo(this.lastX, this.lastY); +// this.ctx.lineTo(x, y); +// this.ctx.stroke(); +// +// this.lastX = x; +// this.lastY = y; +// } + // Dessiner en fonction de la forme sĂ©lectionnĂ©e + draw(event: MouseEvent) { + if (!this.drawing) return; + + const { x, y } = this.getMousePos(event); + const ctx = this.ctx; + + // Effacer ce qui a Ă©tĂ© dessinĂ© pendant le dĂ©placement + //this.drawField(); + + // Si aucune forme n'est sĂ©lectionnĂ©e, on dessine librement + if (this.selectedShape === 'none') { + ctx.strokeStyle = this.selectedColor; + ctx.lineWidth = 5; + ctx.beginPath(); + ctx.moveTo(this.lastX, this.lastY); + ctx.lineTo(x, y); + ctx.stroke(); + } else { + // Sauvegarder le contexte pour ne pas effacer le terrain + ctx.save(); + ctx.strokeStyle = this.selectedColor; + ctx.lineWidth = 5; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + + switch (this.selectedShape) { + case 'rectangle': + ctx.strokeRect(this.startX, this.startY, x - this.startX, y - this.startY); + break; + case 'circle': + const radius = Math.sqrt((x - this.startX) ** 2 + (y - this.startY) ** 2); + ctx.beginPath(); + ctx.arc(this.startX, this.startY, radius, 0, Math.PI * 2); + ctx.stroke(); + break; + case 'line': + ctx.beginPath(); + ctx.moveTo(this.startX, this.startY); + ctx.lineTo(x, y); + ctx.stroke(); + break; + } + + ctx.restore(); + } + + this.lastX = x; + this.lastY = y; + } + + + // ArrĂȘte de dessiner quand la souris est relĂąchĂ©e + stopDrawing() { + this.drawing = false; + } + + // Obtient la position de la souris sur le canvas + private getMousePos(event: MouseEvent) { + const rect = this.canvas.nativeElement.getBoundingClientRect(); + return { + x: event.clientX - rect.left, + y: event.clientY - rect.top, + }; + } + + // MĂ©thode pour effacer le canvas et redessiner le terrain + clearDrawings() { + this.ctx.clearRect(0, + 0, + this.canvas.nativeElement.width, + this.canvas.nativeElement.height); + this.drawField(); // Redessine le terrain de football + } +} diff --git a/src/main.ts b/src/main.ts index 35b00f3..ca05026 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,5 +2,7 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; +// import { AppModule } from './app/app.module'; + bootstrapApplication(AppComponent, appConfig) .catch((err) => console.error(err));