extract rgba resolving to provider
This commit is contained in:
parent
102c084328
commit
e8238c6ea7
|
@ -1,3 +1,5 @@
|
||||||
|
import {useState} from 'react';
|
||||||
|
|
||||||
import ClientScreen from './ClientScreen';
|
import ClientScreen from './ClientScreen';
|
||||||
import Controls from './Controls.tsx';
|
import Controls from './Controls.tsx';
|
||||||
import JoinForm from './JoinForm.tsx';
|
import JoinForm from './JoinForm.tsx';
|
||||||
|
@ -7,38 +9,39 @@ import Row from './components/Row.tsx';
|
||||||
import Scoreboard from './Scoreboard.tsx';
|
import Scoreboard from './Scoreboard.tsx';
|
||||||
import Button from './components/Button.tsx';
|
import Button from './components/Button.tsx';
|
||||||
import MapChooser from './MapChooser.tsx';
|
import MapChooser from './MapChooser.tsx';
|
||||||
|
import {getRandomTheme, RgbaThemeProvider, ThemeContext, useStoredTheme} from './theme.tsx';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import {ThemeContext, getRandomTheme, useStoredTheme} from './theme.ts';
|
|
||||||
import {useState} from 'react';
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [theme, setTheme] = useStoredTheme();
|
const [theme, setTheme] = useStoredTheme();
|
||||||
const [name, setName] = useState<string | null>(null);
|
const [name, setName] = useState<string | null>(null);
|
||||||
|
|
||||||
return <ThemeContext.Provider value={theme}>
|
return <ThemeContext.Provider value={theme}>
|
||||||
<Column className="flex-grow">
|
<RgbaThemeProvider>
|
||||||
|
<Column className="flex-grow">
|
||||||
|
|
||||||
<ClientScreen player={name}/>
|
<ClientScreen player={name}/>
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
<h1 className="flex-grow">CCCB-Tanks!</h1>
|
<h1 className="flex-grow">CCCB-Tanks!</h1>
|
||||||
<MapChooser />
|
<MapChooser/>
|
||||||
<Button text="☼ change colors" onClick={() => setTheme(_ => getRandomTheme())}/>
|
<Button text="☼ change colors" onClick={() => setTheme(_ => getRandomTheme())}/>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => window.open('https://github.com/kaesaecracker/cccb-tanks-cs', '_blank')?.focus()}
|
onClick={() => window.open('https://github.com/kaesaecracker/cccb-tanks-cs', '_blank')?.focus()}
|
||||||
text="⌂ source"/>
|
text="⌂ source"/>
|
||||||
{name !== '' &&
|
{name !== '' &&
|
||||||
<Button onClick={() => setName(_ => '')} text="∩ logout"/>}
|
<Button onClick={() => setName(_ => '')} text="∩ logout"/>}
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
{name || <JoinForm onDone={name => setName(_ => name)}/>}
|
{name || <JoinForm onDone={name => setName(_ => name)}/>}
|
||||||
|
|
||||||
<Row className="GadgetRows">
|
<Row className="GadgetRows">
|
||||||
{name && <Controls player={name}/>}
|
{name && <Controls player={name}/>}
|
||||||
{name && <PlayerInfo player={name}/>}
|
{name && <PlayerInfo player={name}/>}
|
||||||
<Scoreboard/>
|
<Scoreboard/>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
</Column>
|
</Column>
|
||||||
|
</RgbaThemeProvider>
|
||||||
</ThemeContext.Provider>;
|
</ThemeContext.Provider>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {hslToString, ThemeContext} from '../theme.ts';
|
import {useEffect, useRef} from 'react';
|
||||||
import {useContext, useEffect, useRef} from 'react';
|
|
||||||
import './PixelGridCanvas.css';
|
import './PixelGridCanvas.css';
|
||||||
|
import {useRgbaThemeContext} from '../theme.tsx';
|
||||||
|
|
||||||
const pixelsPerRow = 352;
|
const pixelsPerRow = 352;
|
||||||
const pixelsPerCol = 160;
|
const pixelsPerCol = 160;
|
||||||
|
@ -20,12 +20,6 @@ function getPixelDataIndexes(bitIndex: number) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeColor(context: CanvasRenderingContext2D, color: string) {
|
|
||||||
context.fillStyle = color;
|
|
||||||
context.fillRect(0, 0, 1, 1);
|
|
||||||
return context.getImageData(0, 0, 1, 1).data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseAdditionalDataNibble(nibble: number) {
|
function parseAdditionalDataNibble(nibble: number) {
|
||||||
const isPlayerMask = 1;
|
const isPlayerMask = 1;
|
||||||
const entityTypeMask = 12;
|
const entityTypeMask = 12;
|
||||||
|
@ -107,7 +101,7 @@ function drawPixelsToCanvas(
|
||||||
export default function PixelGridCanvas({pixels}: {
|
export default function PixelGridCanvas({pixels}: {
|
||||||
readonly pixels: Uint8ClampedArray;
|
readonly pixels: Uint8ClampedArray;
|
||||||
}) {
|
}) {
|
||||||
const theme = useContext(ThemeContext);
|
const theme = useRgbaThemeContext();
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -137,12 +131,12 @@ export default function PixelGridCanvas({pixels}: {
|
||||||
pixels,
|
pixels,
|
||||||
additional: additionalData,
|
additional: additionalData,
|
||||||
colors: {
|
colors: {
|
||||||
background: normalizeColor(drawContext, hslToString(theme.background)),
|
background: theme.background,
|
||||||
foreground: normalizeColor(drawContext, hslToString(theme.primary)),
|
foreground: theme.primary,
|
||||||
player: normalizeColor(drawContext, hslToString(theme.secondary)),
|
player: theme.secondary,
|
||||||
tanks: normalizeColor(drawContext, hslToString(theme.tertiary)),
|
tanks: theme.tertiary,
|
||||||
powerUps: normalizeColor(drawContext, hslToString(theme.tertiary)),
|
powerUps: theme.tertiary,
|
||||||
bullets: normalizeColor(drawContext, hslToString(theme.tertiary))
|
bullets: theme.tertiary
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {ChangeEventHandler} from "react";
|
import {ChangeEventHandler} from "react";
|
||||||
import './TextInput.css';
|
import './TextInput.css';
|
||||||
|
|
||||||
export default function TextInput(props: {
|
export default function TextInput( {onChange, className, value, placeholder, onEnter }: {
|
||||||
onChange?: ChangeEventHandler<HTMLInputElement> | undefined;
|
onChange?: ChangeEventHandler<HTMLInputElement> | undefined;
|
||||||
className?: string;
|
className?: string;
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -9,13 +9,14 @@ export default function TextInput(props: {
|
||||||
onEnter?: () => void;
|
onEnter?: () => void;
|
||||||
}) {
|
}) {
|
||||||
return <input
|
return <input
|
||||||
{...props}
|
|
||||||
type="text"
|
type="text"
|
||||||
className={'TextInput ' + (props.className?? '')}
|
className={'TextInput ' + (className?? '')}
|
||||||
|
value={value}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChange={onChange}
|
||||||
onKeyUp={event => {
|
onKeyUp={event => {
|
||||||
if (props.onEnter && event.key === 'Enter')
|
if (onEnter && event.key === 'Enter')
|
||||||
props.onEnter();
|
onEnter();
|
||||||
}}
|
}}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import {useStoredObjectState} from './useStoredState.ts';
|
import {useStoredObjectState} from './useStoredState.ts';
|
||||||
import {createContext} from 'react';
|
import {createContext, ReactNode, useContext, useEffect, useRef, useState} from 'react';
|
||||||
|
|
||||||
|
export type HSL = {
|
||||||
|
h: number;
|
||||||
|
s: number;
|
||||||
|
l: number;
|
||||||
|
}
|
||||||
|
|
||||||
export type Theme = {
|
export type Theme = {
|
||||||
primary: HSL;
|
primary: HSL;
|
||||||
|
@ -15,12 +21,6 @@ function getRandom(min: number, max: number) {
|
||||||
return min + Math.random() * (max - min);
|
return min + Math.random() * (max - min);
|
||||||
}
|
}
|
||||||
|
|
||||||
type HSL = {
|
|
||||||
h: number;
|
|
||||||
s: number;
|
|
||||||
l: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRandomHsl(params: {
|
function getRandomHsl(params: {
|
||||||
minHue?: number,
|
minHue?: number,
|
||||||
maxHue?: number,
|
maxHue?: number,
|
||||||
|
@ -91,3 +91,63 @@ export function useStoredTheme() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ThemeContext = createContext<Theme>(getRandomTheme());
|
export const ThemeContext = createContext<Theme>(getRandomTheme());
|
||||||
|
|
||||||
|
type Rgba = Uint8ClampedArray;
|
||||||
|
|
||||||
|
export type RgbaTheme = {
|
||||||
|
primary: Rgba;
|
||||||
|
secondary: Rgba;
|
||||||
|
background: Rgba;
|
||||||
|
tertiary: Rgba;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dummyRgbaTheme: RgbaTheme = {
|
||||||
|
primary: new Uint8ClampedArray([0, 0, 0, 0]),
|
||||||
|
secondary: new Uint8ClampedArray([0, 0, 0, 0]),
|
||||||
|
background: new Uint8ClampedArray([0, 0, 0, 0]),
|
||||||
|
tertiary: new Uint8ClampedArray([0, 0, 0, 0])
|
||||||
|
};
|
||||||
|
|
||||||
|
const RgbaThemeContext = createContext<RgbaTheme>(dummyRgbaTheme);
|
||||||
|
|
||||||
|
function normalizeColor(context: CanvasRenderingContext2D, color: HSL) {
|
||||||
|
context.fillStyle = hslToString(color);
|
||||||
|
context.fillRect(0, 0, 1, 1);
|
||||||
|
return context.getImageData(0, 0, 1, 1).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRgbaThemeContext() {
|
||||||
|
return useContext(RgbaThemeContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RgbaThemeProvider({children}: { children: ReactNode }) {
|
||||||
|
const hslTheme = useContext(ThemeContext);
|
||||||
|
const [rgbaTheme, setRgbaTheme] = useState<RgbaTheme | null>(null);
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
if (canvas === null)
|
||||||
|
throw new Error('canvas null');
|
||||||
|
|
||||||
|
const drawContext = canvas.getContext('2d', {
|
||||||
|
alpha: true,
|
||||||
|
colorSpace: 'srgb',
|
||||||
|
willReadFrequently: true
|
||||||
|
});
|
||||||
|
if (!drawContext)
|
||||||
|
throw new Error('could not get draw context');
|
||||||
|
|
||||||
|
setRgbaTheme({
|
||||||
|
background: normalizeColor(drawContext, hslTheme.background),
|
||||||
|
primary: normalizeColor(drawContext, hslTheme.primary),
|
||||||
|
secondary: normalizeColor(drawContext, hslTheme.secondary),
|
||||||
|
tertiary: normalizeColor(drawContext, hslTheme.tertiary),
|
||||||
|
});
|
||||||
|
}, [hslTheme, canvasRef.current]);
|
||||||
|
|
||||||
|
return <RgbaThemeContext.Provider value={rgbaTheme || dummyRgbaTheme}>
|
||||||
|
<canvas hidden={true} ref={canvasRef}/>
|
||||||
|
{children}
|
||||||
|
</RgbaThemeContext.Provider>;
|
||||||
|
}
|
Loading…
Reference in a new issue