store theme, improved random theme
This commit is contained in:
parent
52e09ae5ef
commit
16d3cd1545
|
@ -10,6 +10,7 @@ import Row from "./components/Row.tsx";
|
|||
import Scoreboard from "./Scoreboard.tsx";
|
||||
import Button from "./components/Button.tsx";
|
||||
import './App.css';
|
||||
import {getRandomTheme, useStoredTheme} from "./theme.ts";
|
||||
|
||||
const getNewNameId = () => ({
|
||||
id: crypto.randomUUID(),
|
||||
|
@ -17,6 +18,7 @@ const getNewNameId = () => ({
|
|||
});
|
||||
|
||||
export default function App() {
|
||||
const [theme, setTheme] = useStoredTheme();
|
||||
const [nameId, setNameId] = useStoredObjectState<NameId>('access', getNewNameId);
|
||||
|
||||
const [isLoggedIn, setLoggedIn] = useState<boolean>(false);
|
||||
|
@ -32,13 +34,14 @@ export default function App() {
|
|||
return <Column className='flex-grow'>
|
||||
<Row>
|
||||
<h1 className='flex-grow'>CCCB-Tanks!</h1>
|
||||
<Button text='change colors' onClick={() => setTheme(getRandomTheme())} />
|
||||
<Button
|
||||
onClick={() => window.open('https://github.com/kaesaecracker/cccb-tanks-cs', '_blank')?.focus()}
|
||||
text='GitHub'/>
|
||||
{nameId.name !== '' &&
|
||||
<Button onClick={() => setNameId(getNewNameId)} text='logout'/>}
|
||||
</Row>
|
||||
<ClientScreen logout={logout}/>
|
||||
<ClientScreen logout={logout} theme={theme}/>
|
||||
{nameId.name === '' && <JoinForm setNameId={setNameId} clientId={nameId.id}/>}
|
||||
{isLoggedIn && <Row className='GadgetRows'>
|
||||
<Controls playerId={nameId.id} logout={logout}/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import useWebSocket from 'react-use-websocket';
|
||||
import {useEffect, useRef} from 'react';
|
||||
import './ClientScreen.css';
|
||||
import {getTheme} from "./theme.ts";
|
||||
import {hslToString, Theme} from "./theme.ts";
|
||||
|
||||
const pixelsPerRow = 352;
|
||||
const pixelsPerCol = 160;
|
||||
|
@ -19,14 +19,13 @@ function normalizeColor(context: CanvasRenderingContext2D, color: string) {
|
|||
return context.getImageData(0, 0, 1, 1).data;
|
||||
}
|
||||
|
||||
function drawPixelsToCanvas(pixels: Uint8Array, canvas: HTMLCanvasElement) {
|
||||
function drawPixelsToCanvas(pixels: Uint8Array, canvas: HTMLCanvasElement, theme: Theme) {
|
||||
const drawContext = canvas.getContext('2d');
|
||||
if (!drawContext)
|
||||
throw new Error('could not get draw context');
|
||||
|
||||
const theme = getTheme();
|
||||
const colorPrimary = normalizeColor(drawContext, theme.primary);
|
||||
const colorBackground = normalizeColor(drawContext, theme.background);
|
||||
const colorPrimary = normalizeColor(drawContext, hslToString(theme.primary));
|
||||
const colorBackground = normalizeColor(drawContext, hslToString(theme.background));
|
||||
|
||||
const imageData = drawContext.getImageData(0, 0, canvas.width, canvas.height, {colorSpace: 'srgb'});
|
||||
const data = imageData.data;
|
||||
|
@ -48,7 +47,7 @@ function drawPixelsToCanvas(pixels: Uint8Array, canvas: HTMLCanvasElement) {
|
|||
drawContext.putImageData(imageData, 0, 0);
|
||||
}
|
||||
|
||||
export default function ClientScreen({logout}: { logout: () => void }) {
|
||||
export default function ClientScreen({logout, theme}: { logout: () => void, theme: Theme }) {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
|
||||
const {
|
||||
|
@ -69,9 +68,9 @@ export default function ClientScreen({logout}: { logout: () => void }) {
|
|||
if (canvasRef.current === null)
|
||||
throw new Error('canvas null');
|
||||
|
||||
drawPixelsToCanvas(new Uint8Array(lastMessage.data), canvasRef.current);
|
||||
drawPixelsToCanvas(new Uint8Array(lastMessage.data), canvasRef.current, theme);
|
||||
sendMessage('');
|
||||
}, [lastMessage, canvasRef.current]);
|
||||
}, [lastMessage, canvasRef.current, theme]);
|
||||
|
||||
return <canvas ref={canvasRef} id="screen" width={pixelsPerRow} height={pixelsPerCol}/>;
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ export default function Scoreboard({}: {}) {
|
|||
className='flex-grow'
|
||||
columns={[
|
||||
{field: 'name'},
|
||||
{field: 'kills', visualize: p => p.scores.kills},
|
||||
{field: 'deaths', visualize: p => p.scores.deaths},
|
||||
{field: 'k/d', visualize: p => p.scores.kills / p.scores.deaths}
|
||||
{field: 'kills', visualize: p => p.scores.kills.toString()},
|
||||
{field: 'deaths', visualize: p => p.scores.deaths.toString()},
|
||||
{field: 'k/d', visualize: p => (p.scores.kills / p.scores.deaths).toString()}
|
||||
]}/>
|
||||
}
|
||||
|
|
|
@ -2,10 +2,6 @@ import React from 'react';
|
|||
import {createRoot} from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from "./App.tsx";
|
||||
import {setRandomTheme} from "./theme.ts";
|
||||
|
||||
|
||||
setRandomTheme();
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
|
|
|
@ -1,44 +1,80 @@
|
|||
function getRandomColor() {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let color = '#';
|
||||
for (let i = 0; i < 3; i++) {
|
||||
color += letters[Math.floor(Math.random() * letters.length)];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
import {useStoredObjectState} from "./useStoredState.ts";
|
||||
|
||||
export type Theme = {
|
||||
primary?: string;
|
||||
secondary?: string;
|
||||
background?: string;
|
||||
primary: HSL;
|
||||
secondary: HSL;
|
||||
background: HSL;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const rootStyle = document.querySelector(':root')?.style;
|
||||
|
||||
export function setTheme(theme: Theme) {
|
||||
if (!rootStyle)
|
||||
return;
|
||||
rootStyle.setProperty('--color-primary', theme.primary);
|
||||
rootStyle.setProperty('--color-secondary', theme.secondary);
|
||||
rootStyle.setProperty('--color-background', theme.background);
|
||||
function getRandom(min: number, max: number) {
|
||||
return min + Math.random() * (max - min);
|
||||
}
|
||||
|
||||
export function getTheme(): Theme {
|
||||
if (!rootStyle)
|
||||
return {};
|
||||
return {
|
||||
primary: rootStyle.getPropertyValue('--color-primary'),
|
||||
secondary: rootStyle.getPropertyValue('--color-secondary'),
|
||||
background: rootStyle.getPropertyValue('--color-background')
|
||||
type HSL = {
|
||||
h: number;
|
||||
s: number;
|
||||
l: number;
|
||||
}
|
||||
|
||||
function getRandomHsl(params: {
|
||||
minHue?: number,
|
||||
maxHue?: number,
|
||||
minSaturation?: number,
|
||||
maxSaturation?: number,
|
||||
minLightness?: number,
|
||||
maxLightness?: number,
|
||||
}): HSL {
|
||||
const values = {
|
||||
minHue: 0,
|
||||
maxHue: 360,
|
||||
minSaturation: 0,
|
||||
maxSaturation: 100,
|
||||
minLightness: 0,
|
||||
maxLightness: 100,
|
||||
...params
|
||||
};
|
||||
const h = getRandom(values.minHue, values.maxHue);
|
||||
const s = getRandom(values.minSaturation, values.maxSaturation);
|
||||
const l = getRandom(values.minLightness, values.maxLightness);
|
||||
return {h, s, l};
|
||||
}
|
||||
|
||||
export function setRandomTheme() {
|
||||
setTheme({
|
||||
primary: getRandomColor(),
|
||||
secondary: getRandomColor(),
|
||||
background: getRandomColor()
|
||||
})
|
||||
export function hslToString({h, s, l}: HSL) {
|
||||
return `hsl(${h},${s}%,${l}%)`;
|
||||
}
|
||||
|
||||
function angle(a: number) {
|
||||
return ((a % 360.0) + 360) % 360;
|
||||
}
|
||||
|
||||
const goldenAngle = 180 * (3 - Math.sqrt(5));
|
||||
|
||||
|
||||
export function getRandomTheme(): Theme {
|
||||
const background = getRandomHsl({maxSaturation: 50, minLightness: 10, maxLightness: 40});
|
||||
|
||||
const primary = getRandomHsl({minSaturation: background.s * 1.2, minLightness: background.l + 20});
|
||||
primary.h = angle(-Math.floor(1 + Math.random() * 2) * goldenAngle + primary.h);
|
||||
|
||||
const secondary = getRandomHsl({minSaturation: background.s * 1.2, minLightness: background.l + 20});
|
||||
primary.h = angle(+Math.floor(1 + Math.random() * 2) * goldenAngle + primary.h);
|
||||
|
||||
return {background, primary, secondary};
|
||||
}
|
||||
|
||||
export function useStoredTheme(): [Theme, (theme: Theme) => void] {
|
||||
const [theme, setSavedTheme] = useStoredObjectState<Theme>('theme', getRandomTheme);
|
||||
|
||||
function setTheme(theme: Theme) {
|
||||
console.log('set theme', theme);
|
||||
rootStyle.setProperty('--color-primary', hslToString(theme.primary));
|
||||
rootStyle.setProperty('--color-secondary', hslToString(theme.secondary));
|
||||
rootStyle.setProperty('--color-background', hslToString(theme.background));
|
||||
setSavedTheme(_ => theme);
|
||||
}
|
||||
|
||||
return [theme, setTheme];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue