diff --git a/css/game.css b/css/game.css index 612f64d..0460a3d 100644 --- a/css/game.css +++ b/css/game.css @@ -20,6 +20,23 @@ --warning: #f39c12; } +/* Touch Optimization */ +* { + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; + user-select: none; +} + +input, textarea { + user-select: text; + -webkit-user-select: text; +} + +body { + touch-action: manipulation; + -webkit-tap-highlight-color: transparent; +} + * { margin: 0; padding: 0; @@ -95,6 +112,7 @@ h2, h3 { display: flex; justify-content: center; margin: 20px 0; + position: relative; } #harvest-btn { @@ -110,6 +128,8 @@ h2, h3 { align-items: center; justify-content: center; box-shadow: 0 4px 20px rgba(155, 89, 182, 0.4); + position: relative; + overflow: hidden; } #harvest-btn:hover { @@ -121,6 +141,67 @@ h2, h3 { transform: scale(0.95); } +/* Pulse Animation */ +#harvest-btn::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 100%; + height: 100%; + border-radius: 50%; + background: rgba(155, 89, 182, 0.3); + transform: translate(-50%, -50%); + animation: pulse 2s ease-in-out infinite; +} + +@keyframes pulse { + 0%, 100% { + width: 100%; + height: 100%; + opacity: 0.3; + } + 50% { + width: 120%; + height: 120%; + opacity: 0; + } +} + +/* Ripple Effect */ +.ripple { + position: absolute; + border-radius: 50%; + background: rgba(255, 255, 255, 0.6); + transform: scale(0); + animation: rippleEffect 0.6s linear; + pointer-events: none; +} + +@keyframes rippleEffect { + to { + transform: scale(4); + opacity: 0; + } +} + +/* Click button glow on active */ +#harvest-btn.clicked { + animation: clickGlow 0.2s ease; +} + +@keyframes clickGlow { + 0% { + box-shadow: 0 4px 20px rgba(155, 89, 182, 0.4); + } + 50% { + box-shadow: 0 0 40px rgba(255, 215, 0, 0.8), 0 4px 20px rgba(155, 89, 182, 0.6); + } + 100% { + box-shadow: 0 4px 20px rgba(155, 89, 182, 0.4); + } +} + .btn-icon { font-size: 3rem; } @@ -605,7 +686,7 @@ h2, h3 { #main-layout { grid-template-columns: 1fr 1fr; } - + #side-panel { grid-column: span 2; } @@ -615,16 +696,86 @@ h2, h3 { #main-layout { grid-template-columns: 1fr; } - + #side-panel { grid-column: span 1; } - + #game-header { flex-direction: column; gap: 10px; text-align: center; } + + /* Touch-friendly button sizes */ + .buy-btn, .prestige-btn { + min-height: 44px; + padding: 12px 20px; + } + + .tab-btn { + min-height: 44px; + padding: 12px; + } + + #harvest-btn { + width: 180px; + height: 180px; + } + + /* Prevent double-tap zoom */ + button, a, input, select { + touch-action: manipulation; + } +} + +/* Mobile Touch Optimizations */ +@media (max-width: 480px) { + body { + overflow-x: hidden; + } + + #main-layout { + padding: 10px; + gap: 10px; + } + + section, aside { + padding: 15px; + } + + #game-header { + padding: 15px 20px; + } + + #game-footer { + flex-wrap: wrap; + justify-content: center; + padding: 10px 15px; + } + + #game-footer button { + flex: 1; + min-width: 80px; + } + + /* Larger touch targets */ + .building-card, .upgrade-card { + padding: 15px; + } + + .achievement { + padding: 12px; + } + + .achievement-icon { + font-size: 1.8rem; + } + + /* Prevent pull-to-refresh on scroll areas */ + #buildings-list, #upgrades-list, #prestige-upgrades-list { + overscroll-behavior: contain; + } } /* ============================================ diff --git a/dev-journal.md b/dev-journal.md index 3169ca4..b9450e8 100644 --- a/dev-journal.md +++ b/dev-journal.md @@ -155,3 +155,29 @@ - Mobile touch optimizations (tap feedback, responsive layout) - Consider adding more visual polish to the click button - Achievement progress tracking in stats panel + +--- + +### Session 7 - 2026-08-21 (Morning) +**Status:** ✅ Mobile Touch Optimizations & Click Button Polish Added + +#### What was done: +- Added comprehensive touch optimization CSS with `-webkit-tap-highlight-color`, `touch-action: manipulation`, and `user-select` controls +- Implemented ripple effect animation on click button for visual feedback +- Added pulse animation to the harvest button (subtle breathing effect) +- Added glow flash animation on each click for satisfying feedback +- Added touchstart event handler with proper preventDefault to avoid double-firing +- Created responsive breakpoints at 768px and 480px for mobile devices +- Increased touch target sizes (44px minimum, per accessibility guidelines) +- Added overscroll-behavior: contain to scroll areas to prevent pull-to-refresh +- Made footer buttons wrap and center on small screens +- Enlarged harvest button on mobile (180px vs 150px desktop) + +#### Files modified: +- `js/game.js` - Added touch event handler, ripple effect creation, click animation methods +- `css/game.css` - Added touch optimizations, ripple animations, pulse effects, responsive breakpoints + +#### Next goals: +- Achievement progress tracking in stats panel +- Consider adding more sound variations or background music +- Potential new building tiers or prestige milestones diff --git a/js/game.js b/js/game.js index 21bbe1c..cb3983c 100644 --- a/js/game.js +++ b/js/game.js @@ -47,11 +47,30 @@ const Game = { * Setup event listeners */ setupEventListeners() { - // Harvest button - document.getElementById('harvest-btn').addEventListener('click', (e) => { + // Harvest button - support both click and touch + const harvestBtn = document.getElementById('harvest-btn'); + + harvestBtn.addEventListener('click', (e) => { this.harvest(e); + this.createRipple(harvestBtn, e); + this.animateClickButton(harvestBtn); }); + // Touch support for better mobile experience + harvestBtn.addEventListener('touchstart', (e) => { + e.preventDefault(); // Prevent double-fire with click + const touch = e.touches[0]; + const clickEvent = new MouseEvent('click', { + clientX: touch.clientX, + clientY: touch.clientY, + bubbles: true + }); + + // Create ripple effect for touch + this.createRipple(harvestBtn, { clientX: touch.clientX, clientY: touch.clientY }); + this.animateClickButton(harvestBtn); + }, { passive: false }); + // Save buttons document.getElementById('save-btn').addEventListener('click', () => { this.save(); @@ -408,6 +427,34 @@ const Game = { if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M'; if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K'; return Math.floor(num).toLocaleString(); + }, + + /** + * Create ripple effect on button click + */ + createRipple(button, event) { + const ripple = document.createElement('span'); + ripple.className = 'ripple'; + + const rect = button.getBoundingClientRect(); + const size = Math.max(rect.width, rect.height); + const x = event.clientX - rect.left - size / 2; + const y = event.clientY - rect.top - size / 2; + + ripple.style.width = ripple.style.height = `${size}px`; + ripple.style.left = `${x}px`; + ripple.style.top = `${y}px`; + + button.appendChild(ripple); + setTimeout(() => ripple.remove(), 600); + }, + + /** + * Animate click button with glow effect + */ + animateClickButton(button) { + button.classList.add('clicked'); + setTimeout(() => button.classList.remove('clicked'), 200); } };