diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ef62921..b4a9414 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,126 +1,15 @@ -import { faCrosshairs } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { CSSProperties, useEffect, useReducer, useState } from 'react'; -import Bluetooth from './Bluetooth'; -import BorderTiles from './components/BorderTiles'; -import FogImages from './components/FogImages'; -import HitElems from './components/HitElems'; -import Labeling from './components/Labeling'; -import Ships from './components/Ships'; -import { hitReducer, initlialTarget, initlialTargetPreview, isHit } from './helpers'; -import { HitType, TargetPreviewType, TargetType } from './interfaces'; +// import Gamefield from './components/Gamefield'; +import Homepage from './components/Homepage'; import './styles/App.scss'; function App() { - const count = 12; - const [target, setTarget] = useState(initlialTarget); - const [targetPreview, setTargetPreview] = useState(initlialTargetPreview); - const [hits, DispatchHits] = useReducer(hitReducer, [] as HitType[]); - - // handle visibility and position change of targetPreview - useEffect(() => { - const { newX, newY, shouldShow, appearOK, eventReady, show, x, y } = targetPreview; - const positionChange = !(x === newX && y === newY); - // if not ready or no new position - if (!eventReady || (!positionChange && show)) - return; - if (show) { - // hide preview to change position when hidden - setTargetPreview(e => ({ ...e, appearOK: false, eventReady: false, show: false })); - } else if (shouldShow && appearOK && !isHit(hits, newX, newY).length) { - // BUT only appear again if it's supposed to (in case the mouse left over the edge) and () - setTargetPreview(e => ({ ...e, appearOK: false, eventReady: false, show: true, x: newX, y: newY })); - } - }, [targetPreview, hits]) - - // enable targetPreview event again after 200 mil. sec. - useEffect(() => { - if (targetPreview.eventReady) - return; - const autoTimeout = setTimeout(() => { - setTargetPreview(e => ({ ...e, eventReady: true })); - }, 200); - - // or abort if state has changed early - return () => { - clearTimeout(autoTimeout); - } - }, [targetPreview.eventReady]); - - // approve targetPreview new position after 200 mil. sec. - useEffect(() => { - // early return to start cooldown only when about to show up - if (!targetPreview.shouldShow) - return; - const autoTimeout = setTimeout(() => { - setTargetPreview(e => ({ ...e, appearOK: true })); - }, 350); - - // or abort if movement is repeated early - return () => { - clearTimeout(autoTimeout); - } - }, [targetPreview.shouldShow, targetPreview.newX, targetPreview.newY]); return (
- -

- {navigator.clipboard.writeText("chrome://flags/#enable-experimental-web-platform-features")}} - // target="_blank" - style={{"cursor": "pointer"}} - // rel="noopener noreferrer" - > - Step 1 - - {" "} - {navigator.clipboard.writeText("chrome://flags/#enable-web-bluetooth-new-permissions-backend")}} - // target="_blank" - style={{"cursor": "pointer"}} - // rel="noopener noreferrer" - - > - Step 2 - -

-
- {/* Bordes */} - - - {/* Collumn lettes and row numbers */} - - - {/* Ships */} - - - - - {/* Fog images */} - {/* */} -
- -
-
- -
-
- {/*

- Edit src/App.tsx and save to reload. -

*/} - -

Battleships designed by macrovector

-
+ + {/* */}
); diff --git a/frontend/src/Bluetooth.tsx b/frontend/src/components/Bluetooth.tsx similarity index 100% rename from frontend/src/Bluetooth.tsx rename to frontend/src/components/Bluetooth.tsx diff --git a/frontend/src/components/Gamefield.tsx b/frontend/src/components/Gamefield.tsx new file mode 100644 index 0000000..9ca0bf5 --- /dev/null +++ b/frontend/src/components/Gamefield.tsx @@ -0,0 +1,126 @@ +import { faCrosshairs } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { CSSProperties, useEffect, useReducer, useState } from 'react'; +import Bluetooth from './Bluetooth'; +import BorderTiles from './BorderTiles'; +import FogImages from './FogImages'; +import HitElems from './HitElems'; +import Labeling from './Labeling'; +import Ships from './Ships'; +import { hitReducer, initlialTarget, initlialTargetPreview, isHit } from '../helpers'; +import { HitType, TargetPreviewType, TargetType } from '../interfaces'; + +function Gamefield() { + + const count = 12; + const [target, setTarget] = useState(initlialTarget); + const [targetPreview, setTargetPreview] = useState(initlialTargetPreview); + const [hits, DispatchHits] = useReducer(hitReducer, [] as HitType[]); + + // handle visibility and position change of targetPreview + useEffect(() => { + const { newX, newY, shouldShow, appearOK, eventReady, show, x, y } = targetPreview; + const positionChange = !(x === newX && y === newY); + // if not ready or no new position + if (!eventReady || (!positionChange && show)) + return; + if (show) { + // hide preview to change position when hidden + setTargetPreview(e => ({ ...e, appearOK: false, eventReady: false, show: false })); + } else if (shouldShow && appearOK && !isHit(hits, newX, newY).length) { + // BUT only appear again if it's supposed to (in case the mouse left over the edge) and () + setTargetPreview(e => ({ ...e, appearOK: false, eventReady: false, show: true, x: newX, y: newY })); + } + }, [targetPreview, hits]) + + // enable targetPreview event again after 200 mil. sec. + useEffect(() => { + if (targetPreview.eventReady) + return; + const autoTimeout = setTimeout(() => { + setTargetPreview(e => ({ ...e, eventReady: true })); + }, 200); + + // or abort if state has changed early + return () => { + clearTimeout(autoTimeout); + } + }, [targetPreview.eventReady]); + + // approve targetPreview new position after 200 mil. sec. + useEffect(() => { + // early return to start cooldown only when about to show up + if (!targetPreview.shouldShow) + return; + const autoTimeout = setTimeout(() => { + setTargetPreview(e => ({ ...e, appearOK: true })); + }, 350); + + // or abort if movement is repeated early + return () => { + clearTimeout(autoTimeout); + } + }, [targetPreview.shouldShow, targetPreview.newX, targetPreview.newY]); + + return ( +
+ +

+ { navigator.clipboard.writeText("chrome://flags/#enable-experimental-web-platform-features") }} + // target="_blank" + style={{ "cursor": "pointer" }} + // rel="noopener noreferrer" + > + Step 1 + + {" "} + { navigator.clipboard.writeText("chrome://flags/#enable-web-bluetooth-new-permissions-backend") }} + // target="_blank" + style={{ "cursor": "pointer" }} + // rel="noopener noreferrer" + + > + Step 2 + +

+
+ {/* Bordes */} + + + {/* Collumn lettes and row numbers */} + + + {/* Ships */} + + + + + {/* Fog images */} + +
+ +
+
+ +
+
+ {/*

+ Edit src/App.tsx and save to reload. +

*/} + +

Battleships designed by macrovector

+
+
+ ) +} + +export default Gamefield \ No newline at end of file diff --git a/frontend/src/components/Homepage.tsx b/frontend/src/components/Homepage.tsx new file mode 100644 index 0000000..2e59734 --- /dev/null +++ b/frontend/src/components/Homepage.tsx @@ -0,0 +1,65 @@ +import { CSSProperties, useEffect, useMemo, useState } from 'react' +import '../styles/home.scss' + +function Homepage() { + + const [columns, setColumns] = useState(floorClient(document.body.clientWidth)) + const [rows, setRows] = useState(floorClient(document.body.clientHeight)) + const [quantity, setQuantity] = useState(columns * rows) + const [position, setPosition] = useState([0, 0]) + const [active, setActve] = useState(false) + + useEffect(() => { + function handleResize() { + setColumns(floorClient(document.body.clientWidth)) + setRows(floorClient(document.body.clientHeight)) + } + handleResize() + window.addEventListener('resize', handleResize) + }, []) + + useEffect(() => { + const timeout = setTimeout(() => setQuantity(columns * rows), 500) + return () => clearTimeout(timeout) + }, [columns, rows]) + + + function floorClient(number: number) { + return Math.floor(number / 50) + } + + function createTile(index: number) { + const x = index % columns + const y = Math.floor(index / columns) + const xDiff = (x - position[0]) / 10 + const yDiff = (y - position[1]) / 10 + const pos = (Math.sqrt(xDiff * xDiff + yDiff * yDiff)).toFixed(2) + return ( +
{ + setPosition([x, y]) + setActve(e => !e) + }} + > + {/* {pos} */} +
+ ) + } + + const createTiles = useMemo(() => { + console.log(3, columns, rows, quantity) + // return

{quantity}

+ return ( +
+ {Array.from(Array(quantity)).map((tile, index) => createTile(index))} +
+ ) + }, [quantity, position]) + + return createTiles +} + +export default Homepage \ No newline at end of file diff --git a/frontend/src/styles/home.scss b/frontend/src/styles/home.scss new file mode 100644 index 0000000..5dcfb90 --- /dev/null +++ b/frontend/src/styles/home.scss @@ -0,0 +1,36 @@ +@use './mixins/effects' as *; + +#tiles { + height: 100vh; + width: 100vw; + display: grid; + grid-template-columns: repeat(var(--columns), 1fr); + grid-template-rows: repeat(var(--rows), 1fr); + + .tile { + @include transition(1s); + outline: 1px solid white; + background-color: black; + + &.active { + animation: bright .5s forwards; + animation-delay: var(--delay); + } + } +} + +@keyframes bright { + 0% { + background-color: black; + } + + 50% { + + background-color: white; + } + + 100% { + + background-color: gray; + } +} \ No newline at end of file