fix: Better animations for actions
This commit is contained in:
@@ -845,18 +845,43 @@ const mapAnimTextByType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers a spinning emoji animation at the centre of the map zone.
|
* Spawns a floating emoji that rises one cell height above the clicked cell
|
||||||
|
* and fades in/out over 1 second.
|
||||||
* @param {"reveal-empty"|"reveal-planet"|"capture"} type
|
* @param {"reveal-empty"|"reveal-planet"|"capture"} type
|
||||||
|
* @param {number} x cell grid column
|
||||||
|
* @param {number} y cell grid row
|
||||||
*/
|
*/
|
||||||
function triggerMapAnimation(type) {
|
function triggerMapAnimation(type, x, y) {
|
||||||
if (!mapAnimEl) return;
|
const cw = SVG_W / GRID_W;
|
||||||
mapAnimEl.textContent = mapAnimTextByType[type] || "🔍";
|
const ch = SVG_H / GRID_H;
|
||||||
mapAnimEl.classList.remove("mapAnim--active");
|
const rect = canvas.getBoundingClientRect();
|
||||||
void mapAnimEl.offsetWidth; // force reflow to restart the animation
|
|
||||||
mapAnimEl.classList.add("mapAnim--active");
|
// Top-centre of the cell in screen pixels, relative to the canvas element
|
||||||
mapAnimEl.addEventListener("animationend", () => {
|
const screenX = ((x * cw + cw / 2 - panX) * zoom / SVG_W) * rect.width;
|
||||||
mapAnimEl.classList.remove("mapAnim--active");
|
const screenY = ((y * ch - panY) * zoom / SVG_H) * rect.height;
|
||||||
}, { once: true });
|
|
||||||
|
// One cell height in screen pixels (travel distance)
|
||||||
|
const cellScreenH = (ch * zoom / SVG_H) * rect.height;
|
||||||
|
const fontSize = Math.max(8, (cw * zoom / SVG_W) * rect.width);
|
||||||
|
|
||||||
|
const el = document.createElement("div");
|
||||||
|
el.className = "mapAnimFloat";
|
||||||
|
el.textContent = mapAnimTextByType[type] || "🔍";
|
||||||
|
el.style.left = `${screenX}px`;
|
||||||
|
el.style.top = `${screenY}px`;
|
||||||
|
el.style.fontSize = `${fontSize}px`;
|
||||||
|
el.style.transform = "translate(-50%, 0)";
|
||||||
|
canvas.parentElement.appendChild(el);
|
||||||
|
|
||||||
|
el.animate(
|
||||||
|
[
|
||||||
|
{ opacity: 0, transform: "translate(-50%, 0)" },
|
||||||
|
{ opacity: 1, transform: `translate(-50%, -${cellScreenH * 0.2}px)`, offset: 0.2 },
|
||||||
|
{ opacity: 1, transform: `translate(-50%, -${cellScreenH * 0.8}px)`, offset: 0.8 },
|
||||||
|
{ opacity: 0, transform: `translate(-50%, -${cellScreenH}px)` },
|
||||||
|
],
|
||||||
|
{ duration: 1000, easing: "ease-out", fill: "forwards" }
|
||||||
|
).onfinish = () => el.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Cursor ────────────────────────────────────────────────────────────────────
|
// ── Cursor ────────────────────────────────────────────────────────────────────
|
||||||
@@ -1062,7 +1087,7 @@ async function onCanvasClick(ev) {
|
|||||||
}
|
}
|
||||||
if (!res.ok) throw new Error("reveal");
|
if (!res.ok) throw new Error("reveal");
|
||||||
applyRevealPayload(await res.json());
|
applyRevealPayload(await res.json());
|
||||||
triggerMapAnimation(cells.get(key)?.hasPlanet ? "reveal-planet" : "reveal-empty");
|
triggerMapAnimation(cells.get(key)?.hasPlanet ? "reveal-planet" : "reveal-empty", cell.x, cell.y);
|
||||||
startCooldown();
|
startCooldown();
|
||||||
updateEconomyDisplay();
|
updateEconomyDisplay();
|
||||||
draw();
|
draw();
|
||||||
@@ -1124,7 +1149,7 @@ async function onCanvasClick(ev) {
|
|||||||
teamActionsRemaining = data.teamActionsRemaining;
|
teamActionsRemaining = data.teamActionsRemaining;
|
||||||
updateTeamQuotaDisplay();
|
updateTeamQuotaDisplay();
|
||||||
}
|
}
|
||||||
triggerMapAnimation("capture");
|
triggerMapAnimation("capture", cell.x, cell.y);
|
||||||
hint.textContent = `🏴 Planète (${cell.x},${cell.y}) capturée !`;
|
hint.textContent = `🏴 Planète (${cell.x},${cell.y}) capturée !`;
|
||||||
showLocalSelection(cell.x, cell.y);
|
showLocalSelection(cell.x, cell.y);
|
||||||
updateEconomyDisplay();
|
updateEconomyDisplay();
|
||||||
|
|||||||
@@ -1299,27 +1299,11 @@ canvas {
|
|||||||
|
|
||||||
/* ── Map action animation ─────────────────────────────────────────────────── */
|
/* ── Map action animation ─────────────────────────────────────────────────── */
|
||||||
|
|
||||||
@keyframes mapAnimPop {
|
.mapAnimFloat {
|
||||||
0% { transform: translate(-50%, -50%) scale(0.1); opacity: 0; }
|
|
||||||
20% { transform: translate(-50%, -50%) scale(1.1); opacity: 1; }
|
|
||||||
80% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
|
|
||||||
100% { transform: translate(-50%, -50%) scale(1.2); opacity: 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapAnim {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
font-size: 88px;
|
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
display: none;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
white-space: nowrap;
|
||||||
|
|
||||||
.mapAnim--active {
|
|
||||||
display: block;
|
|
||||||
animation: mapAnimPop 0.5s ease-out forwards;
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user