mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
feat: view range visualizer
This commit is contained in:
1 parent
2443ecde2f
commit
c174f32e15
9 files changed
+156
-2
No files matched your search
@@ -873,6 +873,13 @@
|
||||
"ricochet": {
|
||||
"safe": "Safe <%s1°",
|
||||
"unsafe": "Unsafe ≥%s1°"
|
||||
},
|
||||
|
||||
"view_range": {
|
||||
"camo": "Camo",
|
||||
"percentage": "%s1%",
|
||||
"desert_sands": "Desert Sands",
|
||||
"new_bay": "New Bay"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 199 KiB |
File renamed without changes.
File renamed without changes.
+145
@@ -0,0 +1,145 @@
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { EyeOpenIcon } from '@radix-ui/react-icons';
|
||||
import { Box, Card, Code, Flex, Slider, Tabs, Text } from '@radix-ui/themes';
|
||||
import { useRef, useState } from 'react';
|
||||
import { Var } from '../../../../../../core/radix/var';
|
||||
import { useLocale } from '../../../../../../hooks/useLocale';
|
||||
import type { VisualizerProps } from './HullTraverseVisualizer';
|
||||
|
||||
const maps = [
|
||||
{
|
||||
id: 'new-bay',
|
||||
string: 'new_bay',
|
||||
size: 512,
|
||||
},
|
||||
{
|
||||
id: 'desert-sands',
|
||||
string: 'desert_sands',
|
||||
size: 512,
|
||||
},
|
||||
] as const;
|
||||
|
||||
export function ViewRangeVisualizer({ stats }: VisualizerProps) {
|
||||
const [mapIndex, setMapIndex] = useState(0);
|
||||
const [camouflage, setCamouflage] = useState(0);
|
||||
const map = maps[mapIndex];
|
||||
const resolvedViewRange = stats.viewRange * (1 - camouflage);
|
||||
const fraction = resolvedViewRange / map.size;
|
||||
const fractionPercentage = `${fraction * 100}%`;
|
||||
const { strings } = useLocale();
|
||||
const container = useRef<HTMLDivElement>(null);
|
||||
const range = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<Card style={{ aspectRatio: '1 / 1' }} mb="6">
|
||||
<Box
|
||||
ref={container}
|
||||
onPointerMove={(event) => {
|
||||
if (!container.current || !range.current) return;
|
||||
|
||||
const rect = container.current.getBoundingClientRect();
|
||||
range.current.style.left = `${event.clientX - rect.left}px`;
|
||||
range.current.style.top = `${event.clientY - rect.top - 16}px`;
|
||||
}}
|
||||
flexGrow="1"
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{
|
||||
backgroundImage: `url(/assets/images/tankopedia/visualizers/range/${map.id}.webp)`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
ref={range}
|
||||
position="absolute"
|
||||
top="50%"
|
||||
left="50%"
|
||||
width={fractionPercentage}
|
||||
align="center"
|
||||
justify="center"
|
||||
style={{
|
||||
aspectRatio: '1 / 1',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
background: `radial-gradient(${Var('gray-a1')}, ${Var('gray-a12')})`,
|
||||
borderRadius: '50%',
|
||||
}}
|
||||
>
|
||||
<Text>
|
||||
<Flex align="center" gap="2">
|
||||
<EyeOpenIcon width="1.25rem" height="1.25rem" />
|
||||
{literals(strings.common.units.m, [resolvedViewRange.toFixed(0)])}
|
||||
</Flex>
|
||||
</Text>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
<Flex
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
width="100%"
|
||||
p="2"
|
||||
gap="3"
|
||||
align="center"
|
||||
style={{
|
||||
background: `linear-gradient(${Var('black-a9')}, ${Var('black-a4')})`,
|
||||
}}
|
||||
>
|
||||
<Text>
|
||||
{strings.website.tools.tankopedia.visualizers.view_range.camo}
|
||||
</Text>
|
||||
|
||||
<Slider
|
||||
variant="classic"
|
||||
value={[camouflage]}
|
||||
min={0}
|
||||
max={0.5}
|
||||
step={Number.EPSILON}
|
||||
onValueChange={([value]) => setCamouflage(value)}
|
||||
/>
|
||||
|
||||
<Code highContrast color="gray" variant="ghost">
|
||||
<Flex justify="center" width="3em">
|
||||
{literals(
|
||||
strings.website.tools.tankopedia.visualizers.view_range
|
||||
.percentage,
|
||||
[`${Math.round(camouflage * 100)}`.padStart(3, ' ')],
|
||||
)}
|
||||
</Flex>
|
||||
</Code>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
justify="center"
|
||||
position="absolute"
|
||||
bottom="0"
|
||||
left="0"
|
||||
width="100%"
|
||||
style={{
|
||||
background: `radial-gradient(at 50% 100%, ${Var('gray-1')}, transparent 70%)`,
|
||||
}}
|
||||
>
|
||||
<Tabs.Root
|
||||
value={`${mapIndex}`}
|
||||
onValueChange={(index) => setMapIndex(Number(index))}
|
||||
>
|
||||
<Tabs.List>
|
||||
{maps.map((map) => (
|
||||
<Tabs.Trigger key={map.id} value={`${maps.indexOf(map)}`}>
|
||||
{
|
||||
strings.website.tools.tankopedia.visualizers.view_range[
|
||||
map.string
|
||||
]
|
||||
}
|
||||
</Tabs.Trigger>
|
||||
))}
|
||||
</Tabs.List>
|
||||
</Tabs.Root>
|
||||
</Flex>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
+4
-2
@@ -34,7 +34,8 @@ import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
||||
import { HullTraverseVisualizer } from './components/HullTraverseVisualizer';
|
||||
import { Info } from './components/Info';
|
||||
import { InfoWithDelta } from './components/InfoWithDelta';
|
||||
import { RicochetVisualizer } from './components/RicochetVisuazlizer';
|
||||
import { RicochetVisualizer } from './components/RicochetVisualizer';
|
||||
import { ViewRangeVisualizer } from './components/ViewRangeVisualizer';
|
||||
|
||||
const [equipmentDefinitions, provisionDefinitions] = await Promise.all([
|
||||
awaitableEquipmentDefinitions,
|
||||
@@ -398,7 +399,7 @@ export function Characteristics() {
|
||||
/>
|
||||
<InfoWithDelta
|
||||
stats={stats}
|
||||
unit="m/s"
|
||||
unit="m"
|
||||
decimals={0}
|
||||
value="shellRange"
|
||||
/>
|
||||
@@ -786,6 +787,7 @@ export function Characteristics() {
|
||||
unit="m"
|
||||
decimals={0}
|
||||
/>
|
||||
<ViewRangeVisualizer stats={stats} />
|
||||
<Info
|
||||
name={
|
||||
strings.website.tools.tankopedia.characteristics.values.camouflage
|
||||
|
||||
Reference in new issue
Block a user