perf: damage rendering in CSS
This commit is contained in:
parent
ebf62613ef
commit
2b91a49997
|
@ -1,72 +1,43 @@
|
|||
import {useEffect, useState} from 'react';
|
||||
|
||||
import {useRadians} from '@/react/context/radians.js';
|
||||
import {memo, useEffect, useState} from 'react';
|
||||
|
||||
import styles from './damage.module.css';
|
||||
|
||||
export default function Damage({
|
||||
function Damage({
|
||||
camera,
|
||||
damage,
|
||||
scale,
|
||||
zIndex,
|
||||
}) {
|
||||
const [hue, setHue] = useState(0);
|
||||
const [opacity, setOpacity] = useState(0.5);
|
||||
const [growth, setGrowth] = useState(0.25);
|
||||
const [offset, setOffset] = useState({x: 0, y: 0});
|
||||
const [randomness] = useState({
|
||||
hue: Math.random() * 10,
|
||||
radians: Math.random() * (Math.PI / 16) - (Math.PI / 32),
|
||||
x: 1 * (Math.random() - 0.5),
|
||||
y: 150 * (Math.random()),
|
||||
rotation: Math.random() * (Math.PI / 8) - (Math.PI / 16),
|
||||
});
|
||||
const radians = useRadians();
|
||||
y: Math.random(),
|
||||
})
|
||||
useEffect(() => {
|
||||
setHue(15 + (Math.cos(radians * 4) * (5 + randomness.hue)));
|
||||
}, [radians, randomness.hue]);
|
||||
useEffect(() => {
|
||||
let handle;
|
||||
let accumulated = 0;
|
||||
let last = Date.now();
|
||||
function float() {
|
||||
const elapsed = (Date.now() - last) / 1000;
|
||||
accumulated += elapsed;
|
||||
last = Date.now();
|
||||
if (accumulated < 0.25) {
|
||||
setOpacity(0.5 + accumulated * 2)
|
||||
}
|
||||
if (accumulated < 0.5) {
|
||||
setGrowth(0.25 + (accumulated * 1.5))
|
||||
setOffset({
|
||||
x: 80 * (accumulated * randomness.x),
|
||||
y: -((80 + randomness.y) * (accumulated)),
|
||||
});
|
||||
}
|
||||
if (accumulated >= 1.5) {
|
||||
damage.onClose();
|
||||
}
|
||||
handle = requestAnimationFrame(float);
|
||||
}
|
||||
handle = requestAnimationFrame(float);
|
||||
const handle = setTimeout(() => {
|
||||
damage.onClose();
|
||||
}, 1_500);
|
||||
return () => {
|
||||
cancelAnimationFrame(handle);
|
||||
clearTimeout(handle);
|
||||
};
|
||||
}, [damage, randomness.x, randomness.y]);
|
||||
|
||||
}, [damage]);
|
||||
const {amount, position} = damage;
|
||||
const left = position.x * scale - camera.x + offset.x;
|
||||
const top = position.y * scale - camera.y + offset.y;
|
||||
return (
|
||||
<div
|
||||
className={styles.damage}
|
||||
style={{
|
||||
color: `hsl(${hue} 100% 50%)`,
|
||||
left: `${left}px`,
|
||||
opacity,
|
||||
top: `${top}px`,
|
||||
transform: `scale(${growth}) rotate(${randomness.rotation}rad)`,
|
||||
'--magnitude': Math.max(1, Math.floor(Math.log10(amount))),
|
||||
'--positionX': `${position.x * scale - camera.x}px`,
|
||||
'--positionY': `${position.y * scale - camera.y}px`,
|
||||
'--randomnessX': randomness.x,
|
||||
'--randomnessY': randomness.y,
|
||||
rotate: `${randomness.radians}rad`,
|
||||
zIndex,
|
||||
}}
|
||||
>
|
||||
<p>{amount}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(Damage);
|
||||
|
|
|
@ -1,25 +1,110 @@
|
|||
@property --hue {
|
||||
initial-value: 0;
|
||||
inherits: false;
|
||||
syntax: '<number>';
|
||||
}
|
||||
@property --opacity {
|
||||
initial-value: 0;
|
||||
inherits: false;
|
||||
syntax: '<number>';
|
||||
}
|
||||
@property --scale {
|
||||
initial-value: 0;
|
||||
inherits: false;
|
||||
syntax: '<number>';
|
||||
}
|
||||
@property --offsetX {
|
||||
initial-value: 0;
|
||||
inherits: false;
|
||||
syntax: '<number>';
|
||||
}
|
||||
@property --offsetY {
|
||||
initial-value: 0;
|
||||
inherits: false;
|
||||
syntax: '<number>';
|
||||
}
|
||||
@property --randomnessX {
|
||||
initial-value: 0;
|
||||
inherits: false;
|
||||
syntax: '<number>';
|
||||
}
|
||||
@property --randomnessY {
|
||||
initial-value: 0;
|
||||
inherits: false;
|
||||
syntax: '<number>';
|
||||
}
|
||||
|
||||
.damage {
|
||||
color: red;
|
||||
overflow-wrap: break-word;
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
margin-right: -66%;
|
||||
text-shadow:
|
||||
0px -1px 0px black,
|
||||
1px 0px 0px black,
|
||||
0px 1px 0px black,
|
||||
-1px 0px 0px black,
|
||||
|
||||
0px -2px 0px black,
|
||||
2px 0px 0px black,
|
||||
0px 2px 0px black,
|
||||
-2px 0px 0px black
|
||||
|
||||
--hue: 0;
|
||||
--opacity: 0.75;
|
||||
--randomnessX: 0;
|
||||
--randomnessY: 0;
|
||||
--scale: 0.35;
|
||||
--background: hsl(var(--hue) 100% 12.5%);
|
||||
--foreground: hsl(var(--hue) 100% 50%);
|
||||
animation:
|
||||
fade 1.5s linear forwards,
|
||||
grow 1.5s ease-in forwards,
|
||||
move 1.5s cubic-bezier(0.5, 1, 0.89, 1),
|
||||
shimmer 250ms infinite ease-in-out
|
||||
;
|
||||
color: var(--foreground);
|
||||
font-size: calc(10px + (var(--magnitude) * 12px));
|
||||
opacity: var(--opacity);
|
||||
overflow-wrap: break-word;
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
text-shadow:
|
||||
0px -1px 0px var(--background),
|
||||
1px 0px 0px var(--background),
|
||||
0px 1px 0px var(--background),
|
||||
-1px 0px 0px var(--background),
|
||||
0px -2px 0px var(--background),
|
||||
2px 0px 0px var(--background),
|
||||
0px 2px 0px var(--background),
|
||||
-2px 0px 0px var(--background)
|
||||
;
|
||||
scale: var(--scale);
|
||||
translate:
|
||||
calc(-50% + (1px * var(--offsetX)) + var(--positionX))
|
||||
calc(-50% + (1px * var(--offsetY)) + var(--positionY))
|
||||
;
|
||||
transform: translate(-50%, -50%);
|
||||
user-select: none;
|
||||
max-width: 66%;
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes move {
|
||||
0% {
|
||||
--offsetX: 0;
|
||||
--offsetY: 0;
|
||||
}
|
||||
33%, 91.6% {
|
||||
--offsetX: calc(80 * var(--randomnessX));
|
||||
--offsetY: calc(-1 * (10 + var(--randomnessY) * 90));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
0% { --opacity: 0.75; }
|
||||
25% { --opacity: 1; }
|
||||
91.6% { --opacity: 1; }
|
||||
100% { --opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes grow {
|
||||
0% { --scale: 0.35; }
|
||||
33% { --scale: 1; }
|
||||
45% { --scale: 1; }
|
||||
40% { --scale: 1.5; }
|
||||
45% { --scale: 1; }
|
||||
91.6% { --scale: 1; }
|
||||
95% { --scale: 0; }
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { --hue: 0 }
|
||||
50% { --hue: 30 }
|
||||
100% { --hue: 0 }
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ export default function Damages({camera, damages, scale}) {
|
|||
damage={damages[key]}
|
||||
key={key}
|
||||
scale={scale}
|
||||
zIndex={key}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
.damages {
|
||||
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
|
||||
font-size: 40px;
|
||||
font-family: Joystix, 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
|
|
@ -31,3 +31,8 @@ body {
|
|||
font-family: "Cookbook";
|
||||
src: url("/assets/fonts/Cookbook.woff") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Joystix";
|
||||
src: url("/assets/fonts/Joystix.ttf") format("ttf");
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ export const {
|
|||
log10,
|
||||
max,
|
||||
min,
|
||||
pow,
|
||||
round,
|
||||
sign,
|
||||
sin,
|
||||
|
@ -317,6 +318,10 @@ export function removeCollinear([...vertices]) {
|
|||
return trimmed;
|
||||
}
|
||||
|
||||
export const smoothstep = (x) => x * x * (3 - 2 * x);
|
||||
|
||||
export const smootherstep = (x) => x * x * x * (x * (x * 6 - 15) + 10);
|
||||
|
||||
export function transform(
|
||||
vertices,
|
||||
{
|
||||
|
|
BIN
public/assets/fonts/Joystix.ttf
Normal file
BIN
public/assets/fonts/Joystix.ttf
Normal file
Binary file not shown.
|
@ -1,4 +1,11 @@
|
|||
const playerEntity = ecs.lookupPlayerEntity(entity.Owned.owner);
|
||||
if (playerEntity !== other && other.Vulnerable) {
|
||||
other.Vulnerable.damage({amount: Math.round(60 + Math.random() * 10), position: other.Position.toJSON()})
|
||||
const magnitude = Math.floor(Math.random() * 3)
|
||||
other.Vulnerable.damage({
|
||||
amount: Math.floor(
|
||||
Math.pow(10, magnitude)
|
||||
+ Math.random() * (Math.pow(10, magnitude + 1) - Math.pow(10, magnitude)),
|
||||
),
|
||||
position: other.Position.toJSON(),
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user