Session 1: Particle effects system with click, building, and achievement visual feedback
This commit is contained in:
@@ -556,6 +556,32 @@ h2, h3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Particle Effects */
|
||||||
|
.particle {
|
||||||
|
will-change: transform, opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ambient Stars Background */
|
||||||
|
.ambient-star {
|
||||||
|
position: absolute;
|
||||||
|
background: radial-gradient(circle, #ffffff 0%, transparent 70%);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: twinkle ease-in-out infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes twinkle {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0.2;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(1.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Scrollbar Styling */
|
/* Scrollbar Styling */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
|
|||||||
@@ -29,3 +29,29 @@ The stats panel tracks and displays:
|
|||||||
- Visual effects (particle effects on click)
|
- Visual effects (particle effects on click)
|
||||||
- Mobile touch optimizations
|
- Mobile touch optimizations
|
||||||
- Prestige/reset system
|
- Prestige/reset system
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Session 1 - 2026-08-19 (14:35 UTC)
|
||||||
|
**Status:** ✅ Particle Effects System Added
|
||||||
|
|
||||||
|
#### What was done:
|
||||||
|
- Created a complete particle effects system for visual feedback
|
||||||
|
- Added click sparkle particles when harvesting stardust
|
||||||
|
- Added building purchase burst effects on the building card
|
||||||
|
- Added achievement unlock celebration effects
|
||||||
|
- Added ambient twinkling stars to the background
|
||||||
|
- Integrated particles into game.js, achievements.js, and main.js
|
||||||
|
|
||||||
|
#### Files modified:
|
||||||
|
- `js/particles.js` - New file with Particle system (click, building, achievement effects + ambient stars)
|
||||||
|
- `js/game.js` - Added particle calls on harvest and buyBuilding
|
||||||
|
- `js/achievements.js` - Added particle effect on achievement unlock
|
||||||
|
- `js/main.js` - Added ambient star creation on init
|
||||||
|
- `css/game.css` - Added particle styles and twinkle animation
|
||||||
|
- `index.html` - Added particles.js script tag
|
||||||
|
|
||||||
|
#### Next goals:
|
||||||
|
- Mobile touch optimizations (tap feedback, responsive layout)
|
||||||
|
- Prestige/reset system with bonuses
|
||||||
|
- Sound effects for clicks and purchases
|
||||||
|
|||||||
@@ -102,6 +102,7 @@
|
|||||||
<script src="js/achievements.js"></script>
|
<script src="js/achievements.js"></script>
|
||||||
<script src="js/ui.js"></script>
|
<script src="js/ui.js"></script>
|
||||||
<script src="js/stats.js"></script>
|
<script src="js/stats.js"></script>
|
||||||
|
<script src="js/particles.js"></script>
|
||||||
<script src="js/main.js"></script>
|
<script src="js/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -164,6 +164,14 @@ const Achievements = {
|
|||||||
if (achievement.condition(Game.state)) {
|
if (achievement.condition(Game.state)) {
|
||||||
Game.state.achievements.push(id);
|
Game.state.achievements.push(id);
|
||||||
UI.showNotification(`Achievement: ${achievement.name}!`, 'achievement');
|
UI.showNotification(`Achievement: ${achievement.name}!`, 'achievement');
|
||||||
|
|
||||||
|
// Particle effect for achievement unlock
|
||||||
|
const countEl = document.querySelector('#achievements-tab h3');
|
||||||
|
if (countEl) {
|
||||||
|
const rect = countEl.getBoundingClientRect();
|
||||||
|
Particles.createAchievementEffect(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`🏆 Achievement unlocked: ${achievement.name}`);
|
console.log(`🏆 Achievement unlocked: ${achievement.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ const Game = {
|
|||||||
|
|
||||||
// Visual feedback
|
// Visual feedback
|
||||||
UI.showFloatingText(event.clientX, event.clientY, `+${this.formatNumber(amount)}`);
|
UI.showFloatingText(event.clientX, event.clientY, `+${this.formatNumber(amount)}`);
|
||||||
|
Particles.createClickParticles(event.clientX, event.clientY, 8);
|
||||||
UI.update();
|
UI.update();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -180,6 +181,14 @@ const Game = {
|
|||||||
this.state.buildings[buildingId] = (this.state.buildings[buildingId] || 0) + 1;
|
this.state.buildings[buildingId] = (this.state.buildings[buildingId] || 0) + 1;
|
||||||
|
|
||||||
UI.showNotification(`Bought ${BUILDINGS[buildingId].name}!`, 'success');
|
UI.showNotification(`Bought ${BUILDINGS[buildingId].name}!`, 'success');
|
||||||
|
|
||||||
|
// Particle effect on building purchase
|
||||||
|
const card = document.getElementById(`building-${buildingId}`);
|
||||||
|
if (card) {
|
||||||
|
const rect = card.getBoundingClientRect();
|
||||||
|
Particles.createBuildingEffect(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
UI.update();
|
UI.update();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
Game.init();
|
Game.init();
|
||||||
|
|
||||||
|
// Create ambient background stars
|
||||||
|
Particles.createAmbientStars(15);
|
||||||
|
|
||||||
// Show last save time
|
// Show last save time
|
||||||
const lastSave = SaveSystem.getLastSaveTime();
|
const lastSave = SaveSystem.getLastSaveTime();
|
||||||
if (lastSave) {
|
if (lastSave) {
|
||||||
|
|||||||
+184
@@ -0,0 +1,184 @@
|
|||||||
|
/**
|
||||||
|
* Cosmic Garden - Particle System
|
||||||
|
* Creates visual particle effects for clicks and achievements
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Particles = {
|
||||||
|
/**
|
||||||
|
* Create sparkle particles on click
|
||||||
|
*/
|
||||||
|
createClickParticles(x, y, count = 8) {
|
||||||
|
const colors = ['#ffd700', '#9b59b6', '#3498db', '#2ecc71', '#ffffff'];
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const particle = document.createElement('div');
|
||||||
|
particle.className = 'particle';
|
||||||
|
|
||||||
|
// Random properties
|
||||||
|
const angle = (Math.PI * 2 * i) / count + (Math.random() - 0.5);
|
||||||
|
const velocity = 50 + Math.random() * 50;
|
||||||
|
const size = 6 + Math.random() * 8;
|
||||||
|
const color = colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
|
||||||
|
particle.style.cssText = `
|
||||||
|
left: ${x}px;
|
||||||
|
top: ${y}px;
|
||||||
|
width: ${size}px;
|
||||||
|
height: ${size}px;
|
||||||
|
background: ${color};
|
||||||
|
border-radius: 50%;
|
||||||
|
pointer-events: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 998;
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(particle);
|
||||||
|
|
||||||
|
// Animate
|
||||||
|
const animation = particle.animate([
|
||||||
|
{
|
||||||
|
transform: 'translate(-50%, -50%) scale(1)',
|
||||||
|
opacity: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: `translate(calc(-50% + ${Math.cos(angle) * velocity}px), calc(-50% + ${Math.sin(angle) * velocity}px)) scale(0)`,
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
], {
|
||||||
|
duration: 600 + Math.random() * 200,
|
||||||
|
easing: 'cubic-bezier(0, 0.9, 0.57, 1)'
|
||||||
|
});
|
||||||
|
|
||||||
|
animation.onfinish = () => particle.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create achievement unlock effect
|
||||||
|
*/
|
||||||
|
createAchievementEffect(x, y) {
|
||||||
|
const colors = ['#ffd700', '#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4'];
|
||||||
|
|
||||||
|
// Burst of particles
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
const particle = document.createElement('div');
|
||||||
|
particle.className = 'particle achievement-particle';
|
||||||
|
|
||||||
|
const angle = (Math.PI * 2 * i) / 20;
|
||||||
|
const velocity = 80 + Math.random() * 60;
|
||||||
|
const size = 8 + Math.random() * 12;
|
||||||
|
const color = colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
|
||||||
|
particle.style.cssText = `
|
||||||
|
left: ${x}px;
|
||||||
|
top: ${y}px;
|
||||||
|
width: ${size}px;
|
||||||
|
height: ${size}px;
|
||||||
|
background: ${color};
|
||||||
|
border-radius: 50%;
|
||||||
|
pointer-events: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 998;
|
||||||
|
box-shadow: 0 0 10px ${color};
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(particle);
|
||||||
|
|
||||||
|
const animation = particle.animate([
|
||||||
|
{
|
||||||
|
transform: 'translate(-50%, -50%) scale(1)',
|
||||||
|
opacity: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: `translate(calc(-50% + ${Math.cos(angle) * velocity}px), calc(-50% + ${Math.sin(angle) * velocity}px)) scale(0.5)`,
|
||||||
|
opacity: 0.8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: `translate(calc(-50% + ${Math.cos(angle) * velocity * 1.5}px), calc(-50% + ${Math.sin(angle) * velocity * 1.5}px)) scale(0)`,
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
], {
|
||||||
|
duration: 1000,
|
||||||
|
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
|
||||||
|
});
|
||||||
|
|
||||||
|
animation.onfinish = () => particle.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create building purchase effect
|
||||||
|
*/
|
||||||
|
createBuildingEffect(x, y) {
|
||||||
|
const colors = ['#2ecc71', '#27ae60', '#1abc9c'];
|
||||||
|
|
||||||
|
for (let i = 0; i < 12; i++) {
|
||||||
|
const particle = document.createElement('div');
|
||||||
|
particle.className = 'particle building-particle';
|
||||||
|
|
||||||
|
const angle = (Math.PI * 2 * i) / 12;
|
||||||
|
const velocity = 60 + Math.random() * 40;
|
||||||
|
const size = 5 + Math.random() * 6;
|
||||||
|
const color = colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
|
||||||
|
particle.style.cssText = `
|
||||||
|
left: ${x}px;
|
||||||
|
top: ${y}px;
|
||||||
|
width: ${size}px;
|
||||||
|
height: ${size}px;
|
||||||
|
background: ${color};
|
||||||
|
border-radius: 2px;
|
||||||
|
pointer-events: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 998;
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(particle);
|
||||||
|
|
||||||
|
const animation = particle.animate([
|
||||||
|
{
|
||||||
|
transform: 'translate(-50%, -50%) rotate(0deg) scale(1)',
|
||||||
|
opacity: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: `translate(calc(-50% + ${Math.cos(angle) * velocity}px), calc(-50% + ${Math.sin(angle) * velocity}px)) rotate(${360 + Math.random() * 180}deg) scale(0)`,
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
], {
|
||||||
|
duration: 700,
|
||||||
|
easing: 'ease-out'
|
||||||
|
});
|
||||||
|
|
||||||
|
animation.onfinish = () => particle.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create ambient floating stars in the background
|
||||||
|
*/
|
||||||
|
createAmbientStars(count = 15) {
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const star = document.createElement('div');
|
||||||
|
star.className = 'ambient-star';
|
||||||
|
|
||||||
|
const size = 2 + Math.random() * 3;
|
||||||
|
const left = Math.random() * 100;
|
||||||
|
const delay = Math.random() * 5;
|
||||||
|
const duration = 3 + Math.random() * 2;
|
||||||
|
|
||||||
|
star.style.cssText = `
|
||||||
|
width: ${size}px;
|
||||||
|
height: ${size}px;
|
||||||
|
left: ${left}%;
|
||||||
|
top: ${Math.random() * 100}%;
|
||||||
|
animation-delay: ${delay}s;
|
||||||
|
animation-duration: ${duration}s;
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.getElementById('game-container').appendChild(star);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make globally accessible
|
||||||
|
window.Particles = Particles;
|
||||||
Reference in New Issue
Block a user