Files

55 lines
2.1 KiB
JavaScript

// Quick feedback animations (0.15 to 0.25 s), so buying feels immediate.
// All of them are plain CSS animations restarted from here; users who prefer
// reduced motion get none (see components.css).
const FALLBACK_MS = 400; // clean-up if the browser never fires animationend
/** Restart a one-shot CSS animation class on an element. */
function play(el, className) {
if (!el) return;
el.classList.remove(className);
void el.offsetWidth; // let the browser notice the class was removed
el.classList.add(className);
const done = () => el.classList.remove(className);
el.addEventListener('animationend', done, { once: true });
setTimeout(done, FALLBACK_MS);
}
/** A block that was just bought: a quick pop and an accent glow. */
export const pop = (el) => play(el, 'fx-pop');
/** A number that just changed: a quick bump. */
export const bump = (el) => play(el, 'fx-bump');
/** A resource that was just spent: a quick flash. */
export const spend = (el) => play(el, 'fx-spend');
/**
* A short "+3" that rises from the middle of `anchor` (or from a point) and fades.
* `at` = { x, y } in viewport coordinates, used when given instead of an element.
* `tone` ('good' | 'bad') colours it green or red; by default it takes the accent colour.
*/
export function floater(anchor, text, at, tone) {
const rect = at ? { left: at.x, top: at.y, width: 0 } : anchor?.getBoundingClientRect();
if (!rect) return;
const el = document.createElement('span');
el.className = tone ? `floater floater--${tone}` : 'floater';
el.textContent = text;
el.style.left = `${rect.left + rect.width / 2}px`;
el.style.top = `${rect.top}px`;
document.body.append(el);
el.addEventListener('animationend', () => el.remove(), { once: true });
setTimeout(() => el.remove(), FALLBACK_MS);
}
/**
* Pop a bought block, float a label above it and flash what was spent (the funds
* in the top bar unless `spentId` says otherwise, or is null for no flash).
*/
export function purchased(el, label, spentId = 'v-funds') {
pop(el);
floater(el, label);
if (spentId) spend(document.getElementById(spentId));
return true;
}