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

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

View File

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

View File

@ -9,6 +9,10 @@ import TimerProvider from "./providers/timer";
import CooldownProvider from "./providers/cooldown";
import CooldownUI from "./ui/cooldown";
import "./objects/common.css";
import OverviewProvider from "./providers/overview";
import OverviewUI from "./ui/overview";
const StageContainer = styled("div")`
width: 100vw;
height: 100vh;
@ -55,20 +59,23 @@ export default function Stage() {
return (
<TimerProvider>
<CooldownProvider>
<StageContainer class="stage" ref={container}>
<Show when={ready()}>
{/* Objects */}
<Crystal size={128} position={centerPosition} />
<Player size={64} position={playerPosition} />
<OverviewProvider>
<CooldownProvider>
<StageContainer class="stage" ref={container}>
<Show when={ready()}>
{/* Objects */}
<Crystal size={128} position={centerPosition} />
<Player size={64} position={playerPosition} />
<Attacker size={32} position={randomPosition} />
<Attacker size={32} position={randomPosition} />
{/* UI Elements */}
<CooldownUI />
</Show>
</StageContainer>
</CooldownProvider>
{/* UI Elements */}
<CooldownUI />
<OverviewUI />
</Show>
</StageContainer>
</CooldownProvider>
</OverviewProvider>
</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 { useTimer } from "../providers/timer";
import { checkElementOverlap } from "./collision";
import { useOverview } from "../providers/overview";
import "./crystal.css";
@ -20,6 +21,8 @@ export default function Crystal(props: { size?: number; position?: vector2d }) {
// Health
const [_, { set }] = useOverview();
const [health, setHealth] = 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);
function calcHostileCauseDamage() {
const units = document.querySelectorAll<HTMLElement>(".hostile");
const units = document.querySelectorAll<HTMLElement>(
".hostile:not([data-dispose=yes])"
);
const crystal = document.querySelector<HTMLElement>(".crystal");
let damage = 0;
for (const unit of units) {
if (crystal && checkElementOverlap(crystal, unit)) {
damage += parseInt(unit.getAttribute("data-damage") ?? "0");
unit.setAttribute("data-need-dispose", "yes");
unit.setAttribute("data-dispose", "yes");
}
}
return damage;
}
function dealHostileUnderCrystal() {
const units = document.querySelectorAll(
".attackers[data-need-dispose=yes]"
);
units.forEach((unit) => unit.remove());
const units = document.querySelectorAll(".attackers[data-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));
}
function updateHealth() {
const damage = calcHostileCauseDamage();
if (damage && isAlive()) {
setHealth(health() - damage);
set("crystal.health", health());
console.log("health: ", health());
}
dealHostileUnderCrystal();

View File

@ -99,7 +99,7 @@ export default function Attacker(props: {
transform: rotate(${facingAngle}deg);
transition-property: transform;
transition-property: transform, opacity;
transition-timing-function: linear;
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>
</>
);
}