mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: start of ricochet visual 2
This commit is contained in:
1 parent
f4f804727f
commit
1b94748668
3 files changed
+281
No files matched your search
+134
@@ -0,0 +1,134 @@
|
||||
@keyframes penetrating-shell-slide {
|
||||
0% {
|
||||
margin-top: -9rem;
|
||||
}
|
||||
28%,
|
||||
100% {
|
||||
margin-top: 8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ricochet-visualizer-penetrating-shell {
|
||||
animation: penetrating-shell-slide infinite;
|
||||
}
|
||||
|
||||
@keyframes ricocheting-shell-slide {
|
||||
0% {
|
||||
margin-top: -18rem;
|
||||
}
|
||||
38% {
|
||||
margin-top: -18rem;
|
||||
}
|
||||
63%,
|
||||
69% {
|
||||
margin-top: 0rem;
|
||||
}
|
||||
100% {
|
||||
margin-top: 8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ricocheting-shell-slide-container {
|
||||
0% {
|
||||
transform: translateX(-50%) rotate(80deg);
|
||||
}
|
||||
63% {
|
||||
transform: translateX(-50%) rotate(80deg);
|
||||
}
|
||||
69% {
|
||||
transform: translateX(-50%) rotate(100deg);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-50%) rotate(100deg);
|
||||
}
|
||||
}
|
||||
|
||||
.ricochet-visualizer-ricocheting-shell {
|
||||
animation: ricocheting-shell-slide infinite;
|
||||
}
|
||||
|
||||
.ricochet-visualizer-ricocheting-shell-container {
|
||||
animation: ricocheting-shell-slide-container infinite;
|
||||
}
|
||||
|
||||
@keyframes surface-impact {
|
||||
0%,
|
||||
60% {
|
||||
opacity: 1;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
66% {
|
||||
opacity: 1;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
72%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ricochet-visualizer-surface-impact {
|
||||
animation: surface-impact linear infinite;
|
||||
}
|
||||
|
||||
@keyframes puncture {
|
||||
0%,
|
||||
10% {
|
||||
opacity: 1;
|
||||
margin-top: -6rem;
|
||||
}
|
||||
15%,
|
||||
30% {
|
||||
opacity: 1;
|
||||
margin-top: -1rem;
|
||||
}
|
||||
45%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
margin-top: -1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ricochet-visualizer-puncture {
|
||||
animation: puncture infinite;
|
||||
}
|
||||
|
||||
@keyframes safe {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
5%,
|
||||
28% {
|
||||
opacity: 1;
|
||||
}
|
||||
33%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ricochet-visualizer-safe {
|
||||
animation: safe infinite;
|
||||
}
|
||||
|
||||
@keyframes unsafe {
|
||||
0%,
|
||||
33% {
|
||||
opacity: 0;
|
||||
}
|
||||
38%,
|
||||
95% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ricochet-visualizer-unsafe {
|
||||
animation: unsafe infinite;
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
import { asset } from '@blitzkit/core';
|
||||
import { Box, Card, Text } from '@radix-ui/themes';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { useRef } from 'react';
|
||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||
import { Var } from '../../../../../../../core/radix/var';
|
||||
import { Duel } from '../../../../../../../stores/duel';
|
||||
import type { VisualizerProps } from '../HullTraverseVisualizer';
|
||||
import './index.css';
|
||||
|
||||
const MAX_THICKNESS = 300;
|
||||
const EXAGGERATION = 4;
|
||||
|
||||
export function RicochetVisualizer2({ stats }: VisualizerProps) {
|
||||
const thickness = 150;
|
||||
const shell = Duel.use((state) => state.protagonist.shell);
|
||||
const container = useRef<HTMLDivElement>(null);
|
||||
const incomingTracer = useRef<HTMLDivElement>(null);
|
||||
const normalizedTracer = useRef<HTMLDivElement>(null);
|
||||
const normalization = EXAGGERATION * degToRad(stats.shellNormalization);
|
||||
|
||||
return (
|
||||
<Card
|
||||
variant="classic"
|
||||
style={{
|
||||
aspectRatio: '1 / 1',
|
||||
}}
|
||||
mb="6"
|
||||
ref={container}
|
||||
onPointerMove={(event) => {
|
||||
if (
|
||||
!container.current ||
|
||||
!incomingTracer.current ||
|
||||
!normalizedTracer.current
|
||||
)
|
||||
return;
|
||||
|
||||
const rect = container.current.getBoundingClientRect();
|
||||
const x = event.clientX - rect.left - rect.width / 2;
|
||||
const y = event.clientY - rect.top - rect.height / 2;
|
||||
const angle = clamp(Math.atan2(y, x), -Math.PI / 2, Math.PI / 2);
|
||||
const normalizedAngle =
|
||||
Math.sign(angle) * Math.max(0, Math.abs(angle) - normalization);
|
||||
|
||||
incomingTracer.current.style.transform = `rotate(${-Math.PI / 2 + angle}rad)`;
|
||||
normalizedTracer.current.style.transform = `rotate(${-Math.PI / 2 + normalizedAngle}rad)`;
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
width={`${(thickness / MAX_THICKNESS) * 50}%`}
|
||||
height="100%"
|
||||
position="absolute"
|
||||
top="0"
|
||||
right="50%"
|
||||
style={{
|
||||
background: `linear-gradient(to right, ${Var('gray-a2')}, ${Var('gray-a4')})`,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{
|
||||
opacity: 2 ** -3,
|
||||
backgroundImage: `url(/assets/images/tankopedia/visualizers/ricochet/armor-hash.png)`,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
ref={incomingTracer}
|
||||
position="absolute"
|
||||
left="50%"
|
||||
top="50%"
|
||||
height="13rem"
|
||||
style={{
|
||||
transformOrigin: 'top center',
|
||||
transform: 'translateX(-50%) rotate(-90deg)',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
position="absolute"
|
||||
width="1px"
|
||||
height="100%"
|
||||
bottom="100%"
|
||||
style={{
|
||||
transform: 'translateX(-50%)',
|
||||
backgroundColor: Var('gray-a5'),
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
position="absolute"
|
||||
width="2px"
|
||||
height="100%"
|
||||
style={{
|
||||
transform: 'translateX(-50%)',
|
||||
background: `linear-gradient(${Var('gray-a11')}, ${Var('gray-a4')})`,
|
||||
}}
|
||||
/>
|
||||
|
||||
<img
|
||||
className="ricochet-visualizer-penetrating-shell"
|
||||
src={asset(`icons/shells/${shell.icon}.webp`)}
|
||||
style={{
|
||||
objectFit: 'contain',
|
||||
transform: 'translate(-50%, -50%) rotate(-45deg)',
|
||||
position: 'absolute',
|
||||
width: '2rem',
|
||||
height: '2rem',
|
||||
top: '6rem',
|
||||
left: 0,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
ref={normalizedTracer}
|
||||
position="absolute"
|
||||
left="50%"
|
||||
top="50%"
|
||||
height="16rem"
|
||||
style={{
|
||||
transformOrigin: 'top center',
|
||||
transform: 'translateX(-50%) rotate(-90deg)',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
position="absolute"
|
||||
width="2px"
|
||||
height="100%"
|
||||
top="-100%"
|
||||
style={{
|
||||
transform: 'translateX(-50%)',
|
||||
background: `linear-gradient(${Var('gray-a4')}, ${Var('gray-a11')})`,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box position="absolute" right="3" bottom="2">
|
||||
<Text style={{ opacity: 0.5 }} size="1">
|
||||
<i>*Not to scale</i>
|
||||
</Text>
|
||||
</Box>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
+2
@@ -35,6 +35,7 @@ import { HullTraverseVisualizer } from './components/HullTraverseVisualizer';
|
||||
import { Info } from './components/Info';
|
||||
import { InfoWithDelta } from './components/InfoWithDelta';
|
||||
import { RicochetVisualizer } from './components/RicochetVisualizer';
|
||||
import { RicochetVisualizer2 } from './components/RicochetVisualizer2';
|
||||
import { ViewRangeVisualizer } from './components/ViewRangeVisualizer';
|
||||
|
||||
const [equipmentDefinitions, provisionDefinitions] = await Promise.all([
|
||||
@@ -389,6 +390,7 @@ export function Characteristics() {
|
||||
value="shellRicochet"
|
||||
/>
|
||||
<RicochetVisualizer stats={stats} />
|
||||
<RicochetVisualizer2 stats={stats} />
|
||||
</>
|
||||
)}
|
||||
<InfoWithDelta
|
||||
|
||||
Reference in new issue
Block a user