perf: damage rendering in CSS

This commit is contained in:
cha0s 2024-07-27 09:47:17 -05:00
parent ebf62613ef
commit 2b91a49997
8 changed files with 143 additions and 70 deletions

View File

@ -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);

View File

@ -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 }
}

View File

@ -11,6 +11,7 @@ export default function Damages({camera, damages, scale}) {
damage={damages[key]}
key={key}
scale={scale}
zIndex={key}
/>
);
}

View File

@ -1,4 +1,3 @@
.damages {
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
font-size: 40px;
font-family: Joystix, 'Courier New', Courier, monospace;
}

View File

@ -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");
}

View File

@ -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,
{

Binary file not shown.

View File

@ -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(),
})
}