Overview UI

This commit is contained in:
LittleSheep 2024-02-16 14:12:34 +08:00
parent cac20e21ad
commit f0af12f4bb
23 changed files with 225 additions and 19 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

112
src/assets/fonts.css Normal file
View File

@ -0,0 +1,112 @@
/* ibm-plex-sans-100 - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 100;
src: url('/fonts/ibm-plex-sans-v19-latin-100.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-100italic - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 100;
src: url('/fonts/ibm-plex-sans-v19-latin-100italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-200 - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 200;
src: url('/fonts/ibm-plex-sans-v19-latin-200.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-200italic - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 200;
src: url('/fonts/ibm-plex-sans-v19-latin-200italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-300 - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 300;
src: url('/fonts/ibm-plex-sans-v19-latin-300.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-300italic - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 300;
src: url('/fonts/ibm-plex-sans-v19-latin-300italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-regular - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
src: url('/fonts/ibm-plex-sans-v19-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-italic - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 400;
src: url('/fonts/ibm-plex-sans-v19-latin-italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-500 - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
src: url('/fonts/ibm-plex-sans-v19-latin-500.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-500italic - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 500;
src: url('/fonts/ibm-plex-sans-v19-latin-500italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-600 - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
src: url('/fonts/ibm-plex-sans-v19-latin-600.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-600italic - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 600;
src: url('/fonts/ibm-plex-sans-v19-latin-600italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-700 - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 700;
src: url('/fonts/ibm-plex-sans-v19-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* ibm-plex-sans-700italic - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 700;
src: url('/fonts/ibm-plex-sans-v19-latin-700italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}

View File

@ -5,3 +5,7 @@
:root { :root {
--color-primary: #49509e; --color-primary: #49509e;
} }
html, body {
font-family: 'IBM Plex Sans', sans;
}

View File

@ -1,5 +1,6 @@
import Stage from "./stage"; import Stage from "./stage";
import "./assets/fonts.css";
import "./index.css"; import "./index.css";
export default function Root() { export default function Root() {

View File

@ -9,6 +9,10 @@ import TimerProvider from "./providers/timer";
import CooldownProvider from "./providers/cooldown"; import CooldownProvider from "./providers/cooldown";
import CooldownUI from "./ui/cooldown"; import CooldownUI from "./ui/cooldown";
import "./objects/common.css";
import OverviewProvider from "./providers/overview";
import OverviewUI from "./ui/overview";
const StageContainer = styled("div")` const StageContainer = styled("div")`
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
@ -55,6 +59,7 @@ export default function Stage() {
return ( return (
<TimerProvider> <TimerProvider>
<OverviewProvider>
<CooldownProvider> <CooldownProvider>
<StageContainer class="stage" ref={container}> <StageContainer class="stage" ref={container}>
<Show when={ready()}> <Show when={ready()}>
@ -66,9 +71,11 @@ export default function Stage() {
{/* UI Elements */} {/* UI Elements */}
<CooldownUI /> <CooldownUI />
<OverviewUI />
</Show> </Show>
</StageContainer> </StageContainer>
</CooldownProvider> </CooldownProvider>
</OverviewProvider>
</TimerProvider> </TimerProvider>
); );
} }

View File

@ -0,0 +1,7 @@
svg {
opacity: 1;
}
svg[data-dispose=yes] {
opacity: 0;
}

View File

@ -3,6 +3,7 @@ import { vector2d } from "../vector";
import { createMemo, createSignal } from "solid-js"; import { createMemo, createSignal } from "solid-js";
import { useTimer } from "../providers/timer"; import { useTimer } from "../providers/timer";
import { checkElementOverlap } from "./collision"; import { checkElementOverlap } from "./collision";
import { useOverview } from "../providers/overview";
import "./crystal.css"; import "./crystal.css";
@ -20,6 +21,8 @@ export default function Crystal(props: { size?: number; position?: vector2d }) {
// Health // Health
const [_, { set }] = useOverview();
const [health, setHealth] = createSignal(100); const [health, setHealth] = createSignal(100);
const [maxHealth, setMaxHealth] = createSignal(100); const [maxHealth, setMaxHealth] = createSignal(100);
@ -28,30 +31,32 @@ export default function Crystal(props: { size?: number; position?: vector2d }) {
const isAlive = createMemo(() => health() > 0); const isAlive = createMemo(() => health() > 0);
function calcHostileCauseDamage() { function calcHostileCauseDamage() {
const units = document.querySelectorAll<HTMLElement>(".hostile"); const units = document.querySelectorAll<HTMLElement>(
".hostile:not([data-dispose=yes])"
);
const crystal = document.querySelector<HTMLElement>(".crystal"); const crystal = document.querySelector<HTMLElement>(".crystal");
let damage = 0; let damage = 0;
for (const unit of units) { for (const unit of units) {
if (crystal && checkElementOverlap(crystal, unit)) { if (crystal && checkElementOverlap(crystal, unit)) {
damage += parseInt(unit.getAttribute("data-damage") ?? "0"); damage += parseInt(unit.getAttribute("data-damage") ?? "0");
unit.setAttribute("data-need-dispose", "yes"); unit.setAttribute("data-dispose", "yes");
} }
} }
return damage; return damage;
} }
function dealHostileUnderCrystal() { function dealHostileUnderCrystal() {
const units = document.querySelectorAll( const units = document.querySelectorAll(".attackers[data-dispose=yes]");
".attackers[data-need-dispose=yes]" // Delay 175ms to remove that node cuz animation is 150ms plus 25ms for browser rerender gap
); units.forEach((unit) => setTimeout(() => unit.remove(), 175));
units.forEach((unit) => unit.remove());
} }
function updateHealth() { function updateHealth() {
const damage = calcHostileCauseDamage(); const damage = calcHostileCauseDamage();
if (damage && isAlive()) { if (damage && isAlive()) {
setHealth(health() - damage); setHealth(health() - damage);
set("crystal.health", health());
console.log("health: ", health()); console.log("health: ", health());
} }
dealHostileUnderCrystal(); dealHostileUnderCrystal();

View File

@ -99,7 +99,7 @@ export default function Attacker(props: {
transform: rotate(${facingAngle}deg); transform: rotate(${facingAngle}deg);
transition-property: transform; transition-property: transform, opacity;
transition-timing-function: linear; transition-timing-function: linear;
transition-duration: 150ms; transition-duration: 150ms;

View File

@ -0,0 +1,36 @@
import { createContext, createSignal, useContext } from "solid-js";
const OverviewContext = createContext();
export default function OverviewProvider(props: any) {
const [stats, setStats] = createSignal<{ [id: string]: any }>({
"crystal.health": 100,
"crystal.maxHealth": 100,
});
const store = [
stats,
{
set(id: string, value: any) {
const original = structuredClone(stats());
original[id] = value;
setStats(original);
},
unset(id: string) {
const original = structuredClone(stats());
delete original[id];
setStats(original);
},
},
];
return (
<OverviewContext.Provider value={store}>
{props.children}
</OverviewContext.Provider>
);
}
export function useOverview(): any[2] {
return useContext(OverviewContext);
}

34
src/stage/ui/overview.tsx Normal file
View File

@ -0,0 +1,34 @@
import { styled } from "solid-styled-components";
import { useOverview } from "../providers/overview";
import { createMemo } from "solid-js";
export default function OverviewUI() {
const [overview] = useOverview();
const crystalHealth = createMemo(() => overview()["crystal.health"]);
const crystalMaxHealth = createMemo(() => overview()["crystal.maxHealth"]);
const regenerationProgress = createMemo(
() => (crystalHealth() / crystalMaxHealth()) * 100
);
const BottomSideContainer = styled("div")`
text-align: center;
position: absolute;
display: flex;
flex-direction: column;
gap: 10px;
width: 100vw;
bottom: 0;
left: 0;
`;
return (
<>
<BottomSideContainer class="healthy-overview">
<div class="text-xl">Crystal {crystalHealth()}/{crystalMaxHealth()}</div>
<progress class="h-[4px] progress progress-error w-screen" value={regenerationProgress()} max={100} />
</BottomSideContainer>
</>
);
}