Skip to content

Commit

Permalink
Merge pull request #42 from La-404-Devinci/feature/canva
Browse files Browse the repository at this point in the history
Feature/canva
  • Loading branch information
Kan-A-Pesh authored Mar 5, 2024
2 parents cf943bd + 5de9d50 commit aa5a3af
Show file tree
Hide file tree
Showing 9 changed files with 385 additions and 109 deletions.
7 changes: 0 additions & 7 deletions frontend/src/App.module.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Inter', sans-serif;
}

.homepage {
display: flex;
flex-direction: column;
Expand Down
251 changes: 149 additions & 102 deletions frontend/src/App.tsx
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

82 changes: 82 additions & 0 deletions frontend/src/components/Canvas.tsx
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>
);
}
10 changes: 10 additions & 0 deletions frontend/src/components/Cursor.tsx
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>
)
}
Loading

0 comments on commit aa5a3af

Please sign in to comment.