diff --git a/landing/app.js b/landing/app.js index fd59d58..fd217ba 100644 --- a/landing/app.js +++ b/landing/app.js @@ -244,7 +244,10 @@ function scrollPhase() { const doc = document.documentElement const max = doc.scrollHeight - window.innerHeight - return clamp(max > 0 ? window.scrollY / max : 0, 0, 1) + const x = clamp(max > 0 ? window.scrollY / max : 0, 0, 1) + // Acelera la transición luz→noche: la fase avanza más deprisa hacia el final + // (el atardecer llega antes, la noche se alcanza antes de llegar abajo del todo). + return 1 - Math.pow(1 - x, 1.3) } /* ── Gráfica del día ── */ @@ -409,6 +412,15 @@ /* ── Batería ── */ const ringFg = $('#ringFg'), batterySoc = $('#batterySoc'), batteryStatus = $('#batteryStatus') + const batTempVal = $('#batTempVal'), batAutonomyVal = $('#batAutonomyVal') + const BATTERY_CAPACITY_KWH = 10 + const fmtAutonomy = (hours) => { + if (hours >= 100) return '∞' + if (hours < 1) return `${Math.max(1, Math.round(hours * 60))} min` + const h = Math.floor(hours), m = Math.round((hours - h) * 60) + if (m >= 60) return `${h + 1} h` + return m ? `${h} h ${m} min` : `${h} h` + } let curSoc = 100 function updateBattery(h) { const st = batteryState(h) @@ -416,6 +428,14 @@ ringFg.style.strokeDashoffset = String(314.16 * (1 - curSoc / 100)) batterySoc.textContent = String(Math.round(curSoc)) batteryStatus.textContent = MICRO[document.documentElement.lang || 'es'][st.mode] + const temp = 23 + (st.mode === 'charge' ? 2.5 : 0) + (st.mode === 'discharge' ? 0.9 : 0) + Math.sin(h * 1.7) * 0.4 + batTempVal.textContent = `${temp.toFixed(1)} °C` + if (st.mode === 'charge') { + batAutonomyVal.textContent = '…' + } else { + const cons = Math.max(0.1, consumptionCurve(h)) + batAutonomyVal.textContent = fmtAutonomy((curSoc / 100) * BATTERY_CAPACITY_KWH / cons) + } } /* ── Flujo de energía (réplica del widget de la app) ── */ @@ -590,6 +610,39 @@ els.forEach((el) => io.observe(el)) } + /* ── Teatro: animación al hacer scroll ── */ + const theaterEl = document.querySelector('.theater') + const theaterStagger = [ + ...document.querySelectorAll('.theater-eyebrow, .theater-title, .theater-sub'), + ...document.querySelectorAll('.theater-widgets .widget'), + ] + const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches + function theaterScroll() { + if (reduceMotion) return + const vh = window.innerHeight + const top = theaterEl.getBoundingClientRect().top + window.scrollY + const start = top - vh + const total = vh + vh * 1.4 + const p = clamp((window.scrollY - start) / total, 0, 1) + let i = 0 + for (const el of theaterStagger) { + const w0 = (i === 0 ? 0 : 0.06 + (i - 1) * 0.11) + const w1 = w0 + 0.12 + const e = ss(w0, w1, p) + el.style.opacity = e.toFixed(3) + el.style.transform = e >= 1 ? '' : `translateY(${((1 - e) * 18).toFixed(1)}px)` + i++ + } + } + function theaterScrollInit() { + if (reduceMotion) return + for (const el of theaterStagger) { + el.style.opacity = '0' + el.style.transform = 'translateY(18px)' + } + theaterScroll() + } + /* ── Init ── */ function init() { const i18n = window.heliosI18n @@ -606,6 +659,7 @@ setInterval(buildTicker, 10000) setupSlider() setupReveal() + theaterScrollInit() updateFlow(hourNow()) let lastDom = 0 @@ -621,6 +675,7 @@ function frame(ts) { const h = hourNow() drawSky(scrollPhase(), h) + theaterScroll() const heroChart = $('#heroChart'), theaterChart = $('#theaterChart'), theaterHist = $('#theaterHist') if (chartNeedsRedraw(heroChart)) drawDayChart(heroChart, true) if (chartNeedsRedraw(theaterChart)) drawDayChart(theaterChart, false) diff --git a/landing/i18n.js b/landing/i18n.js index 4e722b9..a267a4d 100644 --- a/landing/i18n.js +++ b/landing/i18n.js @@ -42,7 +42,7 @@ const I18N = { 'how.s2d': 'Un único servicio Node.js en casa lee por WebSocket los sensores que definas en una topología configurable: N inversores, batería opcional, grid por sensores o scraper.', 'how.s3t': 'Míralo donde quieras', 'how.s3d': 'La app es una PWA responsive: instálala, entra con tu usuario y revisa tu día, tu histórico y tu ahorro desde cualquier pantalla.', - 'how.thanks': 'Gracias, Home Assistant. 💛', + 'how.thanks': 'Gracias, Home Assistant. ❤️', 'how.thanksD': 'Helios no sería posible sin el proyecto Home Assistant y su comunidad. Se conecta a tu HAOS local por WebSocket, sin tocar su API pública, y lee solo los sensores que tú decides.', 'theater.eyebrow': 'LA APP EN MOVIMIENTO', 'theater.t1': 'Mírala respirar.', @@ -55,6 +55,8 @@ const I18N = { 'theater.lCons': 'Consumo', 'theater.bCharged': 'Cargada hoy', 'theater.bDischarged': 'Descargada hoy', + 'theater.bTemp': 'Temperatura', + 'theater.bAutonomy': 'Autonomía', 'theater.bCycles': 'Ciclos', 'theater.bHealth': 'Salud', 'flow.fv': 'Fotovoltaica', @@ -62,7 +64,7 @@ const I18N = { 'flow.battery': 'Batería', 'flow.grid': 'Red', 'screens.eyebrow': 'LA APP', - 'screens.title': 'Hecha para mirarse todos los días.', + 'screens.title': 'Hecha para mirarla todos los días.', 'slide1': 'Vista Hoy · curva en vivo, flujo y totales del día', 'slide2': 'Batería · SOC y curva de carga y descarga', 'slide3': 'Histórico · cientos de días de tu instalación', @@ -147,7 +149,7 @@ const I18N = { 'how.s2d': 'A single Node.js service at home reads over WebSocket the sensors you define in a configurable topology: N inverters, optional battery, grid via sensors or scraper.', 'how.s3t': 'Watch it anywhere', 'how.s3d': 'The app is a responsive PWA: install it, sign in and check your day, your history and your savings from any screen.', - 'how.thanks': 'Thank you, Home Assistant. 💛', + 'how.thanks': 'Thank you, Home Assistant. ❤️', 'how.thanksD': 'Helios would not exist without the Home Assistant project and its community. It connects to your local HAOS over WebSocket, never touches its public API, and reads only the sensors you choose.', 'theater.eyebrow': 'THE APP IN MOTION', 'theater.t1': 'Watch it breathe.', @@ -160,6 +162,8 @@ const I18N = { 'theater.lCons': 'Consumption', 'theater.bCharged': 'Charged today', 'theater.bDischarged': 'Discharged today', + 'theater.bTemp': 'Temperature', + 'theater.bAutonomy': 'Autonomy', 'theater.bCycles': 'Cycles', 'theater.bHealth': 'Health', 'flow.fv': 'Photovoltaic', diff --git a/landing/index.html b/landing/index.html index 2769451..efce8e3 100644 --- a/landing/index.html +++ b/landing/index.html @@ -25,9 +25,9 @@ - - - + + +
Saltar al contenido @@ -209,6 +209,14 @@LA APP
-