mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: better hero
This commit is contained in:
1 parent
cd0769675e
commit
c3e449745e
8 files changed
+176
-206
No files matched your search
@@ -11,6 +11,7 @@ import { useAwait } from '../../../../../hooks/useAwait';
|
|||||||
import * as Duel from '../../../../../stores/duel';
|
import * as Duel from '../../../../../stores/duel';
|
||||||
import * as TankopediaEphemeral from '../../../../../stores/tankopediaEphemeral';
|
import * as TankopediaEphemeral from '../../../../../stores/tankopediaEphemeral';
|
||||||
|
|
||||||
|
const initialPosition = [0, 5, -16] as const;
|
||||||
const poseDistances: Record<Pose, number> = {
|
const poseDistances: Record<Pose, number> = {
|
||||||
[Pose.HullDown]: 15,
|
[Pose.HullDown]: 15,
|
||||||
[Pose.FaceHug]: 5,
|
[Pose.FaceHug]: 5,
|
||||||
@@ -59,7 +60,7 @@ export function Controls() {
|
|||||||
const [autoRotate, setAutoRotate] = useState(true);
|
const [autoRotate, setAutoRotate] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
camera.position.set(-10, 5, -12);
|
camera.position.set(...initialPosition);
|
||||||
orbitControls.current?.target.set(0, 1.25, 0);
|
orbitControls.current?.target.set(0, 1.25, 0);
|
||||||
}, [camera]);
|
}, [camera]);
|
||||||
|
|
||||||
@@ -150,7 +151,7 @@ export function Controls() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Pose.Default: {
|
case Pose.Default: {
|
||||||
camera.position.set(-4, 4, -16);
|
camera.position.set(...initialPosition);
|
||||||
orbitControls.current?.target.set(0, 1.25, 0);
|
orbitControls.current?.target.set(0, 1.25, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export function Lighting() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Environment preset={environment} background={showEnvironment} blur={0} />
|
<Environment preset={environment} background={showEnvironment} blur={0} />
|
||||||
|
|
||||||
<directionalLight
|
<directionalLight
|
||||||
position={[1, 1, -1]}
|
position={[1, 1, -1]}
|
||||||
intensity={hasPbr ? 1 : 5}
|
intensity={hasPbr ? 1 : 5}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { ChevronLeftIcon, MixIcon } from '@radix-ui/react-icons';
|
import { Box, Flex, Heading, Text } from '@radix-ui/themes';
|
||||||
import { Box, Button, Flex, Heading } from '@radix-ui/themes';
|
|
||||||
import Link from 'next/link';
|
|
||||||
import { Suspense, use, useEffect, useMemo, useRef, useState } from 'react';
|
import { Suspense, use, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { ThicknessRange } from '../../../../../../components/Armor/components/StaticArmor';
|
import { ThicknessRange } from '../../../../../../components/Armor/components/StaticArmor';
|
||||||
import { classIcons } from '../../../../../../components/ClassIcon';
|
import { classIcons } from '../../../../../../components/ClassIcon';
|
||||||
|
import { NAVBAR_HEIGHT } from '../../../../../../components/Navbar/index.css';
|
||||||
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
||||||
import { tankDefinitions } from '../../../../../../core/blitzkit/tankDefinitions';
|
import { tankDefinitions } from '../../../../../../core/blitzkit/tankDefinitions';
|
||||||
|
import { Var } from '../../../../../../core/blitzkit/var';
|
||||||
import { useFullScreen } from '../../../../../../hooks/useFullScreen';
|
import { useFullScreen } from '../../../../../../hooks/useFullScreen';
|
||||||
import * as Duel from '../../../../../../stores/duel';
|
import * as Duel from '../../../../../../stores/duel';
|
||||||
import { Options } from './components/Options';
|
|
||||||
import { TankSandbox } from './TankSandbox';
|
import { TankSandbox } from './TankSandbox';
|
||||||
import { TankSandboxLoader } from './TankSandboxLoader';
|
import { TankSandboxLoader } from './TankSandboxLoader';
|
||||||
|
import { Options } from './components/Options';
|
||||||
|
|
||||||
interface HeroSectionProps {
|
interface HeroSectionProps {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -18,13 +18,8 @@ interface HeroSectionProps {
|
|||||||
|
|
||||||
export function HeroSection({ id }: HeroSectionProps) {
|
export function HeroSection({ id }: HeroSectionProps) {
|
||||||
const canvas = useRef<HTMLCanvasElement>(null);
|
const canvas = useRef<HTMLCanvasElement>(null);
|
||||||
const protagonist = Duel.use((state) => state.protagonist.tank);
|
|
||||||
const antagonist = Duel.use((state) => state.antagonist.tank);
|
|
||||||
const compareTanks =
|
|
||||||
protagonist.id === antagonist.id
|
|
||||||
? [protagonist.id]
|
|
||||||
: [protagonist.id, antagonist.id];
|
|
||||||
const isFullScreen = useFullScreen();
|
const isFullScreen = useFullScreen();
|
||||||
|
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||||
const Icon = classIcons[protagonist.class];
|
const Icon = classIcons[protagonist.class];
|
||||||
const [dummyLoader, setDummyLoader] = useState(true);
|
const [dummyLoader, setDummyLoader] = useState(true);
|
||||||
const treeColor =
|
const treeColor =
|
||||||
@@ -58,102 +53,72 @@ export function HeroSection({ id }: HeroSectionProps) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Box
|
||||||
justify="center"
|
style={{
|
||||||
pt={{ initial: '6', md: '0' }}
|
background: isFullScreen
|
||||||
style={{ background: 'var(--color-surface)', position: 'relative' }}
|
? Var('color-background')
|
||||||
height={{ initial: 'calc(75vh - 4rem)', md: undefined }}
|
: Var('color-surface'),
|
||||||
minHeight={{ initial: '40rem' }}
|
zIndex: isFullScreen ? 2 : undefined,
|
||||||
gap="4"
|
}}
|
||||||
|
height={
|
||||||
|
isFullScreen ? '100vh' : `calc(100vh - ${NAVBAR_HEIGHT}px - 8rem)`
|
||||||
|
}
|
||||||
|
width={isFullScreen ? '100vw' : undefined}
|
||||||
|
position={isFullScreen ? 'fixed' : 'relative'}
|
||||||
|
top={isFullScreen ? '0' : undefined}
|
||||||
|
left={isFullScreen ? '0' : undefined}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
direction={{ initial: 'column', md: 'row' }}
|
position="absolute"
|
||||||
style={{ maxWidth: '140rem', flex: 1 }}
|
width="100%"
|
||||||
gap={{ initial: '0', md: '6' }}
|
height="100%"
|
||||||
|
align="center"
|
||||||
|
direction="column"
|
||||||
|
mt="7"
|
||||||
>
|
>
|
||||||
<Flex
|
<Heading
|
||||||
align="center"
|
weight="bold"
|
||||||
justify="center"
|
size={{ initial: '8', xs: '9' }}
|
||||||
flexGrow={{ initial: undefined, md: '1' }}
|
color={treeColor}
|
||||||
flexBasis="0"
|
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex align="center" gap="3">
|
||||||
gap="2"
|
<Icon width="0.75em" height="0.75em" />
|
||||||
direction="column"
|
{tank.name}
|
||||||
ml={{ initial: '0', md: '8' }}
|
|
||||||
align={{ initial: 'center', md: 'start' }}
|
|
||||||
justify="center"
|
|
||||||
style={{ height: '100%', userSelect: 'none' }}
|
|
||||||
>
|
|
||||||
<Flex gap="4">
|
|
||||||
<Heading size={{ initial: '8', sm: '9' }} color={treeColor}>
|
|
||||||
<Icon style={{ width: '0.75em', height: '0.75em' }} />{' '}
|
|
||||||
</Heading>
|
|
||||||
|
|
||||||
<Heading
|
|
||||||
size={{ initial: '8', sm: '9' }}
|
|
||||||
align={{ initial: 'center', md: 'left' }}
|
|
||||||
color={treeColor}
|
|
||||||
>
|
|
||||||
{protagonist.name}
|
|
||||||
</Heading>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex gap="4" mt="2" pl="8">
|
|
||||||
<Link href="/tools/tankopedia">
|
|
||||||
<Button variant="ghost" size="1" ml="-1">
|
|
||||||
<ChevronLeftIcon />
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link href={`/tools/compare?tanks=${compareTanks.join('%2C')}`}>
|
|
||||||
<Button variant="ghost" size="1">
|
|
||||||
<MixIcon />
|
|
||||||
Compare
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Heading>
|
||||||
|
<Text color="gray" weight="light" mt="1">
|
||||||
<Box
|
BlitzKit Tankopedia
|
||||||
className="tank-sandbox-container"
|
</Text>
|
||||||
height={{ initial: undefined, md: '512px' }}
|
|
||||||
style={{
|
|
||||||
flex: 2,
|
|
||||||
position: isFullScreen ? 'fixed' : 'relative',
|
|
||||||
top: isFullScreen ? 0 : undefined,
|
|
||||||
left: isFullScreen ? 0 : undefined,
|
|
||||||
width: isFullScreen ? '100vw' : undefined,
|
|
||||||
height: isFullScreen ? '100vh' : 'auto',
|
|
||||||
zIndex: isFullScreen ? 3 : undefined,
|
|
||||||
background: isFullScreen ? 'var(--color-background)' : undefined,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box width="100%" height="100%" position="absolute">
|
|
||||||
<Box width="100%" height="100%">
|
|
||||||
<TankSandboxLoader
|
|
||||||
id={id}
|
|
||||||
display={dummyLoader ? 'flex' : 'none'}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Suspense
|
|
||||||
fallback={
|
|
||||||
<TankSandboxLoader
|
|
||||||
id={id}
|
|
||||||
display={dummyLoader ? 'none' : 'flex'}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
|
||||||
</Suspense>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Options canvas={canvas} thicknessRange={thicknessRange} />
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
className="tank-sandbox-container"
|
||||||
|
>
|
||||||
|
<Box width="100%" height="100%" position="absolute">
|
||||||
|
<Box width="100%" height="100%">
|
||||||
|
<TankSandboxLoader
|
||||||
|
id={id}
|
||||||
|
display={dummyLoader ? 'flex' : 'none'}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<TankSandboxLoader
|
||||||
|
id={id}
|
||||||
|
display={dummyLoader ? 'none' : 'flex'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
||||||
|
</Suspense>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Options canvas={canvas} thicknessRange={thicknessRange} />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
CameraIcon,
|
CameraIcon,
|
||||||
|
ChevronLeftIcon,
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
EnterFullScreenIcon,
|
EnterFullScreenIcon,
|
||||||
ExitFullScreenIcon,
|
ExitFullScreenIcon,
|
||||||
EyeOpenIcon,
|
EyeOpenIcon,
|
||||||
GearIcon,
|
GearIcon,
|
||||||
|
MixIcon,
|
||||||
} from '@radix-ui/react-icons';
|
} from '@radix-ui/react-icons';
|
||||||
import {
|
import {
|
||||||
Box,
|
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -16,7 +17,6 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
Popover,
|
Popover,
|
||||||
SegmentedControl,
|
SegmentedControl,
|
||||||
Strong,
|
|
||||||
Tabs,
|
Tabs,
|
||||||
Text,
|
Text,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
import { invalidate } from '@react-three/fiber';
|
import { invalidate } from '@react-three/fiber';
|
||||||
import { RefObject, use, useEffect, useState } from 'react';
|
import { RefObject, use, useEffect, useState } from 'react';
|
||||||
import { ThicknessRange } from '../../../../../../../components/Armor/components/StaticArmor';
|
import { ThicknessRange } from '../../../../../../../components/Armor/components/StaticArmor';
|
||||||
|
import { Link } from '../../../../../../../components/Link';
|
||||||
import { ModuleButton } from '../../../../../../../components/ModuleButtons/ModuleButton';
|
import { ModuleButton } from '../../../../../../../components/ModuleButtons/ModuleButton';
|
||||||
import { SmallTankIcon } from '../../../../../../../components/SmallTankIcon';
|
import { SmallTankIcon } from '../../../../../../../components/SmallTankIcon';
|
||||||
import { resolveNearPenetration } from '../../../../../../../core/blitz/resolveNearPenetration';
|
import { resolveNearPenetration } from '../../../../../../../core/blitz/resolveNearPenetration';
|
||||||
@@ -60,6 +61,12 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
const [display, setDisplay] = useState<TankopediaDisplay>(
|
const [display, setDisplay] = useState<TankopediaDisplay>(
|
||||||
TankopediaDisplay.Model,
|
TankopediaDisplay.Model,
|
||||||
);
|
);
|
||||||
|
const antagonist = Duel.use((state) => state.antagonist.tank);
|
||||||
|
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||||
|
const compareTanks =
|
||||||
|
protagonist.id === antagonist.id
|
||||||
|
? [protagonist.id]
|
||||||
|
: [protagonist.id, antagonist.id];
|
||||||
const isFullScreen = useFullScreen();
|
const isFullScreen = useFullScreen();
|
||||||
const showGrid = TankopediaPersistent.use(
|
const showGrid = TankopediaPersistent.use(
|
||||||
(state) => state.model.visual.showGrid,
|
(state) => state.model.visual.showGrid,
|
||||||
@@ -108,31 +115,6 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isFullScreen && (
|
|
||||||
<Box
|
|
||||||
pl="4"
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: 0,
|
|
||||||
top: '50%',
|
|
||||||
transform: 'translateY(-50%)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
size={{
|
|
||||||
initial: '3',
|
|
||||||
xs: '5',
|
|
||||||
}}
|
|
||||||
color="gray"
|
|
||||||
style={{
|
|
||||||
writingMode: 'vertical-lr',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Strong>BlitzKit</Strong> Tankopedia
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<QuickInputs />
|
<QuickInputs />
|
||||||
|
|
||||||
{display === TankopediaDisplay.StaticArmor && (
|
{display === TankopediaDisplay.StaticArmor && (
|
||||||
@@ -360,73 +342,30 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box position="absolute" top="3" right="3">
|
|
||||||
<Popover.Root>
|
|
||||||
<Popover.Trigger>
|
|
||||||
<IconButton size={{ initial: '2', sm: '3' }} variant="soft">
|
|
||||||
<CameraIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Popover.Trigger>
|
|
||||||
|
|
||||||
<Popover.Content>
|
|
||||||
<Flex direction="column" gap="2">
|
|
||||||
<Popover.Close>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
if (!canvas.current) return;
|
|
||||||
|
|
||||||
const anchor = document.createElement('a');
|
|
||||||
|
|
||||||
anchor.setAttribute(
|
|
||||||
'download',
|
|
||||||
`${protagonistTank.name}.png`,
|
|
||||||
);
|
|
||||||
anchor.setAttribute(
|
|
||||||
'href',
|
|
||||||
canvas.current.toDataURL('image/png'),
|
|
||||||
);
|
|
||||||
anchor.click();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DownloadIcon />
|
|
||||||
Download
|
|
||||||
</Button>
|
|
||||||
</Popover.Close>
|
|
||||||
<Popover.Close>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => {
|
|
||||||
if (!canvas.current) return;
|
|
||||||
|
|
||||||
canvas.current.toBlob((blob) => {
|
|
||||||
if (!blob) return;
|
|
||||||
|
|
||||||
navigator.clipboard.write([
|
|
||||||
new ClipboardItem({ 'image/png': blob }),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CopyIcon />
|
|
||||||
Copy
|
|
||||||
</Button>
|
|
||||||
</Popover.Close>
|
|
||||||
</Flex>
|
|
||||||
</Popover.Content>
|
|
||||||
</Popover.Root>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Flex
|
<Flex
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: 16,
|
|
||||||
left: '50%',
|
|
||||||
transform: 'translateX(-50%)',
|
|
||||||
}}
|
|
||||||
direction="column"
|
direction="column"
|
||||||
align="center"
|
align="center"
|
||||||
gap="2"
|
position="absolute"
|
||||||
|
bottom="2"
|
||||||
|
left="50%"
|
||||||
|
style={{ transform: 'translateX(-50%)' }}
|
||||||
>
|
>
|
||||||
|
<Flex gap="4">
|
||||||
|
<Link href="/tools/tankopedia">
|
||||||
|
<Button variant="ghost" size="1">
|
||||||
|
<ChevronLeftIcon />
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link href={`/tools/compare?tanks=${compareTanks.join('%2C')}`}>
|
||||||
|
<Button variant="ghost" size="1">
|
||||||
|
<MixIcon />
|
||||||
|
Compare
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
{display === TankopediaDisplay.DynamicArmor && (
|
{display === TankopediaDisplay.DynamicArmor && (
|
||||||
<Flex align="center" gap="2">
|
<Flex align="center" gap="2">
|
||||||
<Text color="gray" size="2">
|
<Text color="gray" size="2">
|
||||||
@@ -472,7 +411,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Flex gap="3" align="center">
|
<Flex gap="3" align="center" mt="2">
|
||||||
<SegmentedControl.Root
|
<SegmentedControl.Root
|
||||||
value={`${display}`}
|
value={`${display}`}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
@@ -525,6 +464,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
|
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger>
|
<DropdownMenu.Trigger>
|
||||||
<IconButton variant="ghost" color="gray">
|
<IconButton variant="ghost" color="gray">
|
||||||
@@ -621,6 +561,61 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
</DropdownMenu.Sub>
|
</DropdownMenu.Sub>
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
|
|
||||||
|
<Popover.Root>
|
||||||
|
<Popover.Trigger>
|
||||||
|
<IconButton color="gray" variant="ghost">
|
||||||
|
<CameraIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Popover.Trigger>
|
||||||
|
|
||||||
|
<Popover.Content>
|
||||||
|
<Flex direction="column" gap="2">
|
||||||
|
<Popover.Close>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (!canvas.current) return;
|
||||||
|
|
||||||
|
const anchor = document.createElement('a');
|
||||||
|
|
||||||
|
anchor.setAttribute(
|
||||||
|
'download',
|
||||||
|
`${protagonistTank.name}.png`,
|
||||||
|
);
|
||||||
|
anchor.setAttribute(
|
||||||
|
'href',
|
||||||
|
canvas.current.toDataURL('image/png'),
|
||||||
|
);
|
||||||
|
anchor.click();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DownloadIcon />
|
||||||
|
Download
|
||||||
|
</Button>
|
||||||
|
</Popover.Close>
|
||||||
|
<Popover.Close>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
if (!canvas.current) return;
|
||||||
|
|
||||||
|
canvas.current.toBlob((blob) => {
|
||||||
|
if (!blob) return;
|
||||||
|
|
||||||
|
navigator.clipboard.write([
|
||||||
|
new ClipboardItem({ 'image/png': blob }),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CopyIcon />
|
||||||
|
Copy
|
||||||
|
</Button>
|
||||||
|
</Popover.Close>
|
||||||
|
</Flex>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Root>
|
||||||
|
|
||||||
{fullScreenAvailable && (
|
{fullScreenAvailable && (
|
||||||
<IconButton
|
<IconButton
|
||||||
color="gray"
|
color="gray"
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import { style } from '@vanilla-extract/css';
|
|||||||
export const container = style({
|
export const container = style({
|
||||||
display: 'none',
|
display: 'none',
|
||||||
|
|
||||||
|
':focus-within': {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
|
||||||
selectors: {
|
selectors: {
|
||||||
'.tank-sandbox-container:hover &': {
|
'.tank-sandbox-container:hover &': {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ export function QuickInputs() {
|
|||||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||||
const initialGunPitch = tankModelDefinition.turretRotation?.pitch ?? 0;
|
const initialGunPitch = tankModelDefinition.turretRotation?.pitch ?? 0;
|
||||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||||
|
const antagonist = Duel.use((state) => state.antagonist.tank);
|
||||||
|
const compareTanks =
|
||||||
|
protagonist.tank.id === antagonist.id
|
||||||
|
? [protagonist.tank.id]
|
||||||
|
: [protagonist.tank.id, antagonist.id];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
yawInput.current!.value = radToDeg(protagonist.yaw).toFixed(1);
|
yawInput.current!.value = radToDeg(protagonist.yaw).toFixed(1);
|
||||||
@@ -54,17 +59,17 @@ export function QuickInputs() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
className={styles.container}
|
align="center"
|
||||||
|
gap="2"
|
||||||
|
width="10rem"
|
||||||
style={{
|
style={{
|
||||||
width: 128 + 32,
|
|
||||||
pointerEvents: 'auto',
|
pointerEvents: 'auto',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: '50%',
|
left: '50%',
|
||||||
top: 16,
|
top: 16,
|
||||||
transform: 'translateX(-50%)',
|
transform: 'translateX(-50%)',
|
||||||
}}
|
}}
|
||||||
align="center"
|
className={styles.container}
|
||||||
gap="2"
|
|
||||||
>
|
>
|
||||||
<TextField.Root
|
<TextField.Root
|
||||||
size="1"
|
size="1"
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { GameModeSection } from './components/Model/GameModeSection';
|
|||||||
import { HeroSection } from './components/Model/HeroSection';
|
import { HeroSection } from './components/Model/HeroSection';
|
||||||
import { MetaSection } from './components/Model/MetaSection';
|
import { MetaSection } from './components/Model/MetaSection';
|
||||||
import { TechTreeSection } from './components/Model/TechTreeSection';
|
import { TechTreeSection } from './components/Model/TechTreeSection';
|
||||||
import { ShotDisplaySection } from './components/ShotDisplaySection';
|
|
||||||
import { VideoSection } from './components/VideoSection';
|
import { VideoSection } from './components/VideoSection';
|
||||||
|
|
||||||
export default function Page({ params }: { params: { id: string } }) {
|
export default function Page({ params }: { params: { id: string } }) {
|
||||||
@@ -82,11 +81,11 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
return (
|
return (
|
||||||
<PageWrapper p="0" noMaxWidth color="purple" size={1600} gap="9">
|
<PageWrapper p="0" noMaxWidth color="purple" size={1600} gap="9">
|
||||||
<HeroSection id={id} />
|
<HeroSection id={id} />
|
||||||
<ShotDisplaySection />
|
|
||||||
<CalloutsSection />
|
<CalloutsSection />
|
||||||
<MetaSection />
|
{/* <ShotDisplaySection /> */}
|
||||||
{!exempt && <AdMidSectionResponsive />}
|
|
||||||
<TechTreeSection />
|
<TechTreeSection />
|
||||||
|
{!exempt && <AdMidSectionResponsive />}
|
||||||
|
<MetaSection />
|
||||||
<CharacteristicsSection />
|
<CharacteristicsSection />
|
||||||
{!exempt && <AdMidSectionResponsive />}
|
{!exempt && <AdMidSectionResponsive />}
|
||||||
<GameModeSection />
|
<GameModeSection />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { style, styleVariants } from '@vanilla-extract/css';
|
import { style, styleVariants } from '@vanilla-extract/css';
|
||||||
|
|
||||||
const MIN_WIDTH = 640 + 128;
|
const MIN_WIDTH = 640 + 128;
|
||||||
const HEIGHT = 48;
|
export const NAVBAR_HEIGHT = 48;
|
||||||
|
|
||||||
export const hamburger = style({
|
export const hamburger = style({
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
@@ -24,7 +24,7 @@ export const tools = style({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const navbar = style({
|
export const navbar = style({
|
||||||
height: HEIGHT,
|
height: NAVBAR_HEIGHT,
|
||||||
marginBottom: -1, // 1px overlap with content to bleed color
|
marginBottom: -1, // 1px overlap with content to bleed color
|
||||||
position: 'sticky',
|
position: 'sticky',
|
||||||
top: 0,
|
top: 0,
|
||||||
@@ -42,7 +42,7 @@ const navbarExpanderBase = style({
|
|||||||
|
|
||||||
'@media': {
|
'@media': {
|
||||||
[`screen and (min-width: ${MIN_WIDTH}px)`]: {
|
[`screen and (min-width: ${MIN_WIDTH}px)`]: {
|
||||||
maxHeight: HEIGHT,
|
maxHeight: NAVBAR_HEIGHT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -51,7 +51,7 @@ export const navbarExpander = styleVariants({
|
|||||||
false: [
|
false: [
|
||||||
navbarExpanderBase,
|
navbarExpanderBase,
|
||||||
{
|
{
|
||||||
maxHeight: HEIGHT,
|
maxHeight: NAVBAR_HEIGHT,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
true: [
|
true: [
|
||||||
|
|||||||
Reference in new issue
Block a user