From f6ff4122e2e7de87c7b1ed9a9a587d6cf25a08a7 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sat, 2 May 2026 21:19:48 +0200 Subject: [PATCH] Klima: Achievement-Tap-Tooltip (Touch hat keinen Hover) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tap auf eine Erfolgs-Badge öffnet einen kleinen Tooltip mit Titel + Beschreibung. Verschwindet nach 4 s oder beim nächsten Tap außerhalb. Position bevorzugt unter der Badge, mit Fallback nach oben falls unten kein Platz. --- App/sims/klima/game-2d.html | 56 +++++++++++++++++++++++++++++++++++++ App/sims/klima/game-3d.html | 55 ++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/App/sims/klima/game-2d.html b/App/sims/klima/game-2d.html index e8c1f8c..7f7d3b2 100644 --- a/App/sims/klima/game-2d.html +++ b/App/sims/klima/game-2d.html @@ -587,6 +587,23 @@ } } +/* Achievement-Tooltip: title-Attribute öffnen sich auf Touch nicht; daher + ein eigenes Tap-Popup mit Titel + Beschreibung. */ +.kw-ach-tooltip { + position: fixed; + z-index: 1500; + max-width: 240px; + background: var(--ggs-fjord-dark); + color: var(--ggs-white); + padding: 8px 12px; + border-radius: var(--ggs-radius-sm); + font-size: 13px; + line-height: 1.4; + box-shadow: var(--ggs-shadow-md); + pointer-events: none; + animation: ggs-fade-in 0.15s var(--ggs-ease) both; +} + @keyframes kw-build-slidein { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } @@ -1728,11 +1745,50 @@ function renderAchievements(force) { if (!has) classes.push('kw-ach-locked'); el.className = classes.join(' '); el.title = a.title + (has ? ' — ' + a.desc : ' (noch nicht)'); + el.dataset.achTitle = a.title; + el.dataset.achDesc = has ? a.desc : 'Noch nicht freigeschaltet.'; el.textContent = has ? a.icon : '?'; host.appendChild(el); }); } +/* Achievement-Tap-Tooltip — title greift auf Touch nicht. Tap auf eine + Badge zeigt einen kleinen schwarzen Tooltip mit Titel + Beschreibung, + verschwindet nach 4 s oder beim nächsten Tap außerhalb. */ +let _achTooltip = null; +let _achTooltipTimer = 0; +function _hideAchTooltip() { + if (_achTooltipTimer) { clearTimeout(_achTooltipTimer); _achTooltipTimer = 0; } + if (_achTooltip) { _achTooltip.remove(); _achTooltip = null; } +} +document.addEventListener('click', (e) => { + if (_achTooltip && !_achTooltip.contains(e.target) && !e.target.closest('.ggs-badge-earned')) { + _hideAchTooltip(); + } + const badge = e.target.closest('.ggs-badge-earned'); + if (!badge) return; + const title = badge.dataset.achTitle || badge.title || ''; + const desc = badge.dataset.achDesc || ''; + if (!title) return; + _hideAchTooltip(); + const t = document.createElement('div'); + t.className = 'kw-ach-tooltip'; + t.innerHTML = '' + title.replace(/[<>&]/g, c => ({'<':'<','>':'>','&':'&'}[c])) + '' + + (desc ? '
' + desc.replace(/[<>&]/g, c => ({'<':'<','>':'>','&':'&'}[c])) : ''); + document.body.appendChild(t); + _achTooltip = t; + const rect = badge.getBoundingClientRect(); + const tw = t.offsetWidth; + const th = t.offsetHeight; + const left = Math.max(8, Math.min(rect.left + rect.width / 2 - tw / 2, window.innerWidth - tw - 8)); + // Bevorzugt unter der Badge; falls dort kein Platz, nach oben. + let top = rect.bottom + 8; + if (top + th > window.innerHeight - 8) top = Math.max(8, rect.top - th - 8); + t.style.left = left + 'px'; + t.style.top = top + 'px'; + _achTooltipTimer = setTimeout(_hideAchTooltip, 4000); +}); + /* ============================================================ UI RENDERING ============================================================ */ diff --git a/App/sims/klima/game-3d.html b/App/sims/klima/game-3d.html index 45b1e49..4d1eed9 100644 --- a/App/sims/klima/game-3d.html +++ b/App/sims/klima/game-3d.html @@ -715,6 +715,23 @@ } } +/* Achievement-Tooltip: title-Attribute öffnen sich auf Touch nicht; daher + ein eigenes Tap-Popup mit Titel + Beschreibung. */ +.kw-ach-tooltip { + position: fixed; + z-index: 1500; + max-width: 240px; + background: var(--ggs-fjord-dark); + color: var(--ggs-white); + padding: 8px 12px; + border-radius: var(--ggs-radius-sm); + font-size: 13px; + line-height: 1.4; + box-shadow: var(--ggs-shadow-md); + pointer-events: none; + animation: ggs-fade-in 0.15s var(--ggs-ease) both; +} + @keyframes kw-build-slidein { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } @@ -2036,11 +2053,49 @@ function renderAchievements(force) { if (!has) classes.push('kw-ach-locked'); el.className = classes.join(' '); el.title = a.title + (has ? ' — ' + a.desc : ' (noch nicht)'); + el.dataset.achTitle = a.title; + el.dataset.achDesc = has ? a.desc : 'Noch nicht freigeschaltet.'; el.textContent = has ? a.icon : '?'; host.appendChild(el); }); } +/* Achievement-Tap-Tooltip — title greift auf Touch nicht. Tap auf eine + Badge zeigt einen kleinen schwarzen Tooltip mit Titel + Beschreibung, + verschwindet nach 4 s oder beim nächsten Tap außerhalb. */ +let _achTooltip = null; +let _achTooltipTimer = 0; +function _hideAchTooltip() { + if (_achTooltipTimer) { clearTimeout(_achTooltipTimer); _achTooltipTimer = 0; } + if (_achTooltip) { _achTooltip.remove(); _achTooltip = null; } +} +document.addEventListener('click', (e) => { + if (_achTooltip && !_achTooltip.contains(e.target) && !e.target.closest('.ggs-badge-earned')) { + _hideAchTooltip(); + } + const badge = e.target.closest('.ggs-badge-earned'); + if (!badge) return; + const title = badge.dataset.achTitle || badge.title || ''; + const desc = badge.dataset.achDesc || ''; + if (!title) return; + _hideAchTooltip(); + const t = document.createElement('div'); + t.className = 'kw-ach-tooltip'; + t.innerHTML = '' + title.replace(/[<>&]/g, c => ({'<':'<','>':'>','&':'&'}[c])) + '' + + (desc ? '
' + desc.replace(/[<>&]/g, c => ({'<':'<','>':'>','&':'&'}[c])) : ''); + document.body.appendChild(t); + _achTooltip = t; + const rect = badge.getBoundingClientRect(); + const tw = t.offsetWidth; + const th = t.offsetHeight; + const left = Math.max(8, Math.min(rect.left + rect.width / 2 - tw / 2, window.innerWidth - tw - 8)); + let top = rect.bottom + 8; + if (top + th > window.innerHeight - 8) top = Math.max(8, rect.top - th - 8); + t.style.left = left + 'px'; + t.style.top = top + 'px'; + _achTooltipTimer = setTimeout(_hideAchTooltip, 4000); +}); + /* ============================================================ UI RENDERING ============================================================ */