feat: building purchase particle burst effects (#57) #72
@@ -96,6 +96,8 @@ body{background:var(--bg);color:var(--text);font-family:'SF Mono','Cascadia Code
|
||||
@keyframes toast-in{from{transform:translateX(40px);opacity:0}to{transform:translateX(0);opacity:0.95}}
|
||||
@keyframes toast-out{from{opacity:0.95;transform:translateX(0)}to{opacity:0;transform:translateX(40px)}}
|
||||
@keyframes res-pulse{0%{transform:scale(1)}50%{transform:scale(1.18);color:#80d0ff}100%{transform:scale(1)}}
|
||||
.particle{position:fixed;pointer-events:none;z-index:150;border-radius:50%;animation:particle-fly 0.6s ease-out forwards}
|
||||
@keyframes particle-fly{0%{opacity:1;transform:translate(0,0) scale(1)}100%{opacity:0;transform:translate(var(--px),var(--py)) scale(0.2)}}
|
||||
@keyframes res-shake{0%,100%{transform:translateX(0)}20%{transform:translateX(-3px)}40%{transform:translateX(3px)}60%{transform:translateX(-2px)}80%{transform:translateX(2px)}}
|
||||
.r-val.pulse{animation:res-pulse 0.35s ease-out}
|
||||
.r-val.shake{animation:res-shake 0.3s ease-out;color:var(--red)!important}
|
||||
|
||||
12
js/engine.js
12
js/engine.js
@@ -303,6 +303,12 @@ function buyBuilding(id) {
|
||||
updateRates();
|
||||
const label = qty > 1 ? `x${qty}` : '';
|
||||
log(`Built ${def.name} ${label} (total: ${G.buildings[id]})`);
|
||||
// Particle burst on purchase
|
||||
const btn = document.querySelector('[onclick="buyBuilding(\'' + id + '\')"]');
|
||||
if (btn) {
|
||||
const rect = btn.getBoundingClientRect();
|
||||
spawnParticles(rect.left + rect.width / 2, rect.top + rect.height / 2, '#4a9eff', 10);
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
@@ -329,6 +335,12 @@ function buyProject(id) {
|
||||
}
|
||||
|
||||
updateRates();
|
||||
// Gold particle burst on project completion
|
||||
const pBtn = document.querySelector('[onclick="buyProject(\'' + id + '\')"]');
|
||||
if (pBtn) {
|
||||
const rect = pBtn.getBoundingClientRect();
|
||||
spawnParticles(rect.left + rect.width / 2, rect.top + rect.height / 2, '#ffd700', 16);
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
|
||||
25
js/utils.js
25
js/utils.js
@@ -285,6 +285,31 @@ function getClickPower() {
|
||||
return (1 + Math.floor(G.buildings.autocoder * 0.5) + Math.max(0, (G.phase - 1)) * 2) * G.codeBoost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a burst of particles at (x, y) for visual feedback.
|
||||
* @param {number} x - Center X in viewport pixels.
|
||||
* @param {number} y - Center Y in viewport pixels.
|
||||
* @param {string} color - Particle color (CSS value).
|
||||
* @param {number} [count=12] - Number of particles.
|
||||
*/
|
||||
function spawnParticles(x, y, color, count) {
|
||||
count = count || 12;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'particle';
|
||||
const size = 3 + Math.random() * 4;
|
||||
const angle = (Math.PI * 2 * i / count) + (Math.random() - 0.5) * 0.5;
|
||||
const dist = 30 + Math.random() * 40;
|
||||
const px = Math.cos(angle) * dist;
|
||||
const py = Math.sin(angle) * dist;
|
||||
el.style.cssText =
|
||||
'left:' + x + 'px;top:' + y + 'px;width:' + size + 'px;height:' + size +
|
||||
'px;background:' + color + ';--px:' + px + 'px;--py:' + py + 'px';
|
||||
document.body.appendChild(el);
|
||||
setTimeout(function() { el.remove(); }, 650);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates production rates for all resources based on buildings and boosts.
|
||||
*/
|
||||
Reference in New Issue
Block a user