-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #42 from La-404-Devinci/feature/canva
Feature/canva
- Loading branch information
Showing
9 changed files
with
385 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,102 +1,149 @@ | ||
import { useEffect, useState } from 'react' | ||
import styles from './App.module.css' | ||
import LoginComponent from './components/login' | ||
import ProfilComponent from './components/profil' | ||
import LeaderboardComponent from './components/leaderboard' | ||
import { socket } from './socket'; | ||
import ChatComponent from './components/chat' | ||
import isMobile from './utiles/isMobile' | ||
import classementItem from '../../common/interfaces/classementItem.interface' | ||
|
||
function App() { | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const [classement, setClassement] = useState<classementItem[]>([]) | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const [isConnected, setIsConnected] = useState(socket.connected); | ||
|
||
const [userEmail, setUserEmail] = useState(""); | ||
const [displayBtnLogin, setDisplayBtnLogin] = useState(true); | ||
const [displayComponent, setDisplayComponent] = useState("none"); | ||
const [isMobileView, setIsMobileView] = useState(isMobile.any()) | ||
|
||
|
||
useEffect(() => { | ||
function onConnect() { | ||
setIsConnected(true); | ||
} | ||
|
||
function onDisconnect() { | ||
setIsConnected(false); | ||
} | ||
|
||
function onclassementUpdate(data: classementItem[]) { | ||
setClassement(data) | ||
} | ||
|
||
socket.on('connect', onConnect); | ||
socket.on('disconnect', onDisconnect); | ||
socket.on('classementUpdate', onclassementUpdate); | ||
|
||
return () => { | ||
socket.off('connect', onConnect); | ||
socket.off('disconnect', onDisconnect); | ||
socket.off('classementUpdate', onclassementUpdate); | ||
}; | ||
}, []); | ||
|
||
useEffect(() => { | ||
const handleResize = () => { | ||
setIsMobileView(isMobile.any()) | ||
}; | ||
|
||
window.addEventListener('resize', handleResize) | ||
|
||
return () => { | ||
window.removeEventListener('resize', handleResize) | ||
}; | ||
}, []) | ||
|
||
|
||
const handleLogin = (email: string) => { | ||
setUserEmail(email.split('@')[0]); | ||
setDisplayBtnLogin(false); | ||
} | ||
|
||
const handleDisplayComponent = (componentName: string) => { | ||
if (isMobileView == true) { | ||
if (displayComponent === componentName) { | ||
setDisplayComponent("none"); | ||
} else { | ||
setDisplayComponent(componentName); | ||
} | ||
} else { | ||
if (displayComponent === componentName) { | ||
setDisplayComponent("none"); | ||
} else { | ||
setDisplayComponent(componentName); | ||
} | ||
} | ||
} | ||
|
||
|
||
// affichage (render) | ||
return ( | ||
<div className={styles.homepage}> | ||
<div className={styles.containerTop}> | ||
{isMobile.any() && <button onClick={() => handleDisplayComponent("chat")} className={styles.btnChat}><img src="/src/assets/message.svg" alt="icone-chat" /></button>} | ||
{displayComponent !== "profil" && <button onClick={() => handleDisplayComponent("profil")} className={styles.btnProfil}><img src="/src/assets/user-large.svg" alt="icone-user-profil" /></button>} | ||
</div> | ||
|
||
<LeaderboardComponent /> | ||
|
||
{displayBtnLogin && <button onClick={() => handleDisplayComponent("login")} className={styles.btnLogin}>Login to draw !</button>} | ||
|
||
{displayComponent === "login" && <LoginComponent onLogin={handleLogin} />} | ||
{displayComponent === "profil" && <ProfilComponent userEmail={userEmail} onHideProfil={() => handleDisplayComponent("none")} />} | ||
{displayComponent === "chat" && <ChatComponent />} | ||
{!isMobile.any() && <ChatComponent userEmail={userEmail} />} | ||
</div> | ||
); | ||
} | ||
|
||
export default App | ||
// import { useState } from 'react' | ||
import { SetStateAction, useEffect, useState } from 'react'; | ||
import styles from './App.module.css' | ||
import { socket } from './socket'; | ||
import classementItem from '../../common/interfaces/classementItem.interface' | ||
import ChatComponent from './components/chat' | ||
import LeaderboardComponent from './components/leaderboard' | ||
import LoginComponent from './components/login' | ||
import ProfilComponent from './components/profil' | ||
import isMobile from './utiles/isMobile' | ||
import Canvas from './components/Canvas' | ||
import Palette from './components/Palette'; | ||
import Timer from './components/Timer'; | ||
|
||
function App() { | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const [classement, setClassement] = useState<classementItem[]>([]) | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const [isConnected, setIsConnected] = useState(socket.connected); | ||
|
||
|
||
const [selectedColor, setSelectedColor] = useState("white"); | ||
|
||
const [zoom, setZoom] = useState(1); | ||
|
||
const [userEmail, setUserEmail] = useState(""); | ||
const [displayBtnLogin, setDisplayBtnLogin] = useState(true); | ||
const [displayComponent, setDisplayComponent] = useState("none"); | ||
const [isMobileView, setIsMobileView] = useState(isMobile.any()) | ||
|
||
useEffect(() => { | ||
function onConnect() { | ||
setIsConnected(true); | ||
} | ||
|
||
function onDisconnect() { | ||
setIsConnected(false); | ||
} | ||
|
||
function onclassementUpdate(data: classementItem[]) { | ||
setClassement(data) | ||
} | ||
|
||
socket.on('connect', onConnect); | ||
socket.on('disconnect', onDisconnect); | ||
socket.on('classementUpdate', onclassementUpdate); | ||
|
||
return () => { | ||
socket.off('connect', onConnect); | ||
socket.off('disconnect', onDisconnect); | ||
socket.off('classementUpdate', onclassementUpdate); | ||
}; | ||
}, []); | ||
|
||
|
||
|
||
const handleColorSelect = (color: SetStateAction<string>) => { | ||
setSelectedColor(color); | ||
console.log('Couleur sélectionnée dans App.tsx:', color); | ||
}; | ||
|
||
|
||
|
||
useEffect(() => { | ||
const handleWheel = (event: WheelEvent) => { | ||
// Multiplicateur de zoom arbitraire | ||
const zoomFactor = 0.1; | ||
// Si la molette de la souris est déplacée vers le haut, zoom avant, sinon zoom arrière | ||
const newZoom = event.deltaY > 0 ? zoom - zoomFactor : zoom + zoomFactor; | ||
// Limiter le zoom à un minimum de 0.1 pour éviter les valeurs non valides | ||
setZoom(Math.max(0.1, newZoom)); | ||
}; | ||
|
||
window.addEventListener('wheel', handleWheel); | ||
|
||
return () => { | ||
window.removeEventListener('wheel', handleWheel); | ||
}; | ||
}, [zoom]); | ||
|
||
useEffect(() => { | ||
const handleResize = () => { | ||
setIsMobileView(isMobile.any()) | ||
}; | ||
|
||
window.addEventListener('resize', handleResize) | ||
|
||
return () => { | ||
window.removeEventListener('resize', handleResize) | ||
}; | ||
}, []) | ||
|
||
const handleLogin = (email: string) => { | ||
setUserEmail(email.split('@')[0]); | ||
setDisplayBtnLogin(false); | ||
} | ||
|
||
const handleDisplayComponent = (componentName: string) => { | ||
if (isMobileView == true) { | ||
if (displayComponent === componentName) { | ||
setDisplayComponent("none"); | ||
} else { | ||
setDisplayComponent(componentName); | ||
} | ||
} else { | ||
if (displayComponent === componentName) { | ||
setDisplayComponent("none"); | ||
} else { | ||
setDisplayComponent(componentName); | ||
} | ||
} | ||
} | ||
|
||
|
||
|
||
// affichage (render) | ||
return ( | ||
<div> | ||
<div className={styles.testCanvas}> | ||
<Canvas actualColor={selectedColor} zoom={zoom} /> | ||
<Palette onColorClick={handleColorSelect} /> | ||
<Timer /> | ||
</div> | ||
|
||
{/* <div id="test-login"> | ||
<LoginComponent /> | ||
</div> */} | ||
|
||
<div className={styles.homepage}> | ||
<div className={styles.containerTop}> | ||
{isMobile.any() && <button onClick={() => handleDisplayComponent("chat")} className={styles.btnChat}><img src="/src/assets/message.svg" alt="icone-chat" /></button>} | ||
{displayComponent !== "profil" && <button onClick={() => handleDisplayComponent("profil")} className={styles.btnProfil}><img src="/src/assets/user-large.svg" alt="icone-user-profil" /></button>} | ||
</div> | ||
|
||
<LeaderboardComponent /> | ||
|
||
{displayBtnLogin && <button onClick={() => handleDisplayComponent("login")} className={styles.btnLogin}>Login to draw !</button>} | ||
|
||
{displayComponent === "login" && <LoginComponent onLogin={handleLogin} />} | ||
{displayComponent === "profil" && <ProfilComponent userEmail={userEmail} onHideProfil={() => handleDisplayComponent("none")} />} | ||
{displayComponent === "chat" && <ChatComponent />} | ||
{!isMobile.any() && <ChatComponent userEmail={userEmail} />} | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default App | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import React, { useEffect, useRef } from 'react'; | ||
import { useState } from 'react'; | ||
import styles from '../App.module.css'; | ||
|
||
export default function Canvas(props: {actualColor: string, zoom: number}) { | ||
const canvasRef = useRef<HTMLCanvasElement>(null); // Référence pour le canvas | ||
const cursorRef = useRef<HTMLDivElement>(null); // Référence pour le curseur | ||
|
||
const [pixelSize, setPixelWidth] = useState(20); | ||
|
||
const zoom = props.zoom; | ||
|
||
useEffect(() => { | ||
// Accéder aux éléments DOM à travers les refs | ||
const cursor = cursorRef.current; | ||
if (cursor) { | ||
cursor.style.background = props.actualColor; | ||
} | ||
}, [props.actualColor]); | ||
|
||
function getCursorPosition(event: React.MouseEvent<HTMLElement>) { | ||
const canvas = canvasRef.current; | ||
if (!canvas) return [0, 0]; | ||
|
||
const relativeX = event.clientX - canvas.getBoundingClientRect().left; | ||
const relativeY = event.clientY - canvas.getBoundingClientRect().top; | ||
|
||
const zoomX = relativeX / zoom; | ||
const zoomY = relativeY / zoom; | ||
|
||
const pixelX = Math.floor(zoomX / pixelSize); | ||
const pixelY = Math.floor(zoomY / pixelSize); | ||
|
||
return [pixelX, pixelY]; | ||
} | ||
|
||
function handleClick(event: React.MouseEvent<HTMLElement>) { | ||
const canvas = canvasRef.current; | ||
if (!canvas) return; | ||
|
||
const ctx = canvas.getContext('2d'); | ||
if (!ctx) return; | ||
|
||
ctx.beginPath(); | ||
ctx.fillStyle = props.actualColor; | ||
|
||
const [pixelX, pixelY] = getCursorPosition(event); | ||
const x = pixelX * pixelSize; | ||
const y = pixelY * pixelSize; | ||
|
||
ctx.fillRect(x, y, pixelSize, pixelSize); | ||
} | ||
|
||
function handleMove(event: React.MouseEvent<HTMLCanvasElement>) { | ||
const [pixelX, pixelY] = getCursorPosition(event); | ||
const cursor = cursorRef.current; | ||
|
||
if (cursor) { | ||
cursor.style.left = pixelX * pixelSize + 'px'; | ||
cursor.style.top = pixelY * pixelSize + 'px'; | ||
} | ||
} | ||
|
||
return ( | ||
<div className={styles.canvas} style={{transform: `scale(${zoom})`}}> | ||
<div | ||
ref={cursorRef} | ||
className={styles.cursor} | ||
style={{width: pixelSize, height: pixelSize}} | ||
onMouseDown={handleClick as React.MouseEventHandler<HTMLDivElement>} | ||
></div> | ||
<canvas | ||
ref={canvasRef} | ||
className={styles.game} | ||
width={500} | ||
height={500} | ||
onMouseMove={handleMove} | ||
onMouseDown={handleClick} | ||
></canvas> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import React from 'react' | ||
import styles from '../App.module.css' | ||
|
||
export default function Cursor() { | ||
return ( | ||
<div className={styles.cursor}> | ||
|
||
</div> | ||
) | ||
} |
Oops, something went wrong.