Feature: Phone-Frame mit 9 App-Screenshots im Hero — SW by-v449, APP_VER 428

- 9 Screenshots synchron mit Hero-Text (Fade gemeinsam)
- CSS-Phone-Frame, Statusleiste per margin-top abgeschnitten
- /img Static-Mount in main.py
- Screenshots als JPEG optimiert (8MB → 2.5MB gesamt)
- Vorladen aller Folgebilder beim Start
This commit is contained in:
rene 2026-04-27 19:38:43 +02:00
parent 3b9f69fa0b
commit ddfc20b10d
13 changed files with 45 additions and 13 deletions

View file

@ -220,6 +220,7 @@ STATIC_DIR = os.path.join(os.path.dirname(__file__), "static")
app.mount("/css", StaticFiles(directory=f"{STATIC_DIR}/css"), name="css") app.mount("/css", StaticFiles(directory=f"{STATIC_DIR}/css"), name="css")
app.mount("/js", StaticFiles(directory=f"{STATIC_DIR}/js"), name="js") app.mount("/js", StaticFiles(directory=f"{STATIC_DIR}/js"), name="js")
app.mount("/icons", StaticFiles(directory=f"{STATIC_DIR}/icons"), name="icons") app.mount("/icons", StaticFiles(directory=f"{STATIC_DIR}/icons"), name="icons")
app.mount("/img", StaticFiles(directory=f"{STATIC_DIR}/img"), name="img")
# User-generierte Medien (Fotos aus Tagebuch, Giftköder-Alarm, etc.) # User-generierte Medien (Fotos aus Tagebuch, Giftköder-Alarm, etc.)
MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung. Router, State-Management, Navigation, Initialisierung.
============================================================ */ ============================================================ */
const APP_VER = '427'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VER = '428'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const App = (() => { const App = (() => {

View file

@ -13,15 +13,15 @@ window.Page_welcome = (() => {
// HERO-SLIDES — rotieren alle 4 Sekunden // HERO-SLIDES — rotieren alle 4 Sekunden
// ---------------------------------------------------------- // ----------------------------------------------------------
const HERO_SLIDES = [ const HERO_SLIDES = [
{ headline: 'Jeder Moment zählt.', sub: 'Fotos, Notizen, Stimmungen — das Tagebuch deines Hundes.' }, { headline: 'Jeder Moment zählt.', sub: 'Fotos, Notizen, Stimmungen — das Tagebuch deines Hundes.', screen: '/img/screenshots/screen-1.jpg' },
{ headline: 'Deine Gegend. Sein Revier.', sub: 'Hundeparks, Gassi-Spots und mehr — alles auf der Karte.' }, { headline: 'Deine Gegend. Sein Revier.', sub: 'Hundeparks, Gassi-Spots und mehr — alles auf der Karte.', screen: '/img/screenshots/screen-2.jpg' },
{ headline: 'Kein Termin verpasst.', sub: 'Impfungen, Gewicht, Tierarzt — mit KI, individuell auf deinen Hund angepasst.' }, { headline: 'Kein Termin verpasst.', sub: 'Impfungen, Gewicht, Tierarzt — mit KI, individuell auf deinen Hund angepasst.', screen: '/img/screenshots/screen-3.jpg' },
{ headline: 'Wir achten auf deinen Hund.', sub: 'Gefahren in deiner Nähe — damit ihr gezielt aus dem Weg gehen könnt.' }, { headline: 'Wir achten auf deinen Hund.', sub: 'Gefahren in deiner Nähe — damit ihr gezielt aus dem Weg gehen könnt.', screen: '/img/screenshots/screen-4.jpg' },
{ headline: 'Wie eine App. Nur ohne App Store.', sub: 'Einmal auf „Zum Homescreen" — fertig. Kein Store, keine Updates, kein Stress.' }, { headline: 'Wie eine App. Nur ohne App Store.', sub: 'Einmal auf „Zum Homescreen" — fertig. Kein Store, keine Updates.', screen: '/img/screenshots/screen-5.jpg' },
{ headline: 'Lieblingsrouten für immer.', sub: 'Speichere eure besten Strecken — und entdecke neue in der Nähe.' }, { headline: 'Lieblingsrouten für immer.', sub: 'Speichere eure besten Strecken — und entdecke neue in der Nähe.', screen: '/img/screenshots/screen-6.jpg' },
{ headline: 'Gassi ist kein Solosport.', sub: 'Triff andere Hundebesitzer — spontan, in deiner Umgebung.' }, { headline: 'Gassi ist kein Solosport.', sub: 'Triff andere Hundebesitzer — spontan, in deiner Umgebung.', screen: '/img/screenshots/screen-7.jpg' },
{ headline: 'Dein virtueller Trainer.', sub: '100+ Übungen, Schritt für Schritt — individuell auf deinen Hund abgestimmt.' }, { headline: 'Dein virtueller Trainer.', sub: '100+ Übungen, Schritt für Schritt — individuell auf deinen Hund abgestimmt.', screen: '/img/screenshots/screen-8.jpg' },
{ headline: 'Frag nach. Du bist nicht allein.', sub: 'Erfahrungen, Tipps, Hilfe — von Hundebesitzern für Hundebesitzer.' }, { headline: 'Frag nach. Du bist nicht allein.', sub: 'Erfahrungen, Tipps, Hilfe — von Hundebesitzern für Hundebesitzer.', screen: '/img/screenshots/screen-9.jpg' },
]; ];
async function init(container, appState, params = {}) { async function init(container, appState, params = {}) {
@ -99,6 +99,11 @@ window.Page_welcome = (() => {
<p class="wc-lhero-sub" id="wc-hero-sub">${HERO_SLIDES[0].sub}</p> <p class="wc-lhero-sub" id="wc-hero-sub">${HERO_SLIDES[0].sub}</p>
<span class="wc-hero-counter" id="wc-hero-counter">1 / ${HERO_SLIDES.length}</span> <span class="wc-hero-counter" id="wc-hero-counter">1 / ${HERO_SLIDES.length}</span>
<div class="wc-phone-frame">
<img class="wc-phone-screen" id="wc-phone-screen"
src="${HERO_SLIDES[0].screen}" alt="" loading="eager">
</div>
<div class="wc-lhero-cta"> <div class="wc-lhero-cta">
${hasPrompt ? ` ${hasPrompt ? `
<button class="btn wc-btn-hero" id="welcome-install-hero-btn"> <button class="btn wc-btn-hero" id="welcome-install-hero-btn">
@ -346,6 +351,23 @@ window.Page_welcome = (() => {
font-weight: var(--weight-semibold); letter-spacing: 0.08em; font-weight: var(--weight-semibold); letter-spacing: 0.08em;
margin-bottom: var(--space-5); position: relative; margin-bottom: var(--space-5); position: relative;
} }
.wc-phone-frame {
width: 188px;
border-radius: 30px;
border: 3px solid rgba(255,255,255,0.28);
box-shadow: 0 24px 64px rgba(0,0,0,0.45), 0 0 0 1px rgba(0,0,0,0.12);
overflow: hidden;
margin: 0 auto var(--space-5);
position: relative;
background: #111;
}
.wc-phone-screen {
width: 100%;
display: block;
margin-top: -24px; /* iOS-Statusleiste abschneiden */
transition: opacity 0.4s ease;
}
.wc-lhero-cta { .wc-lhero-cta {
display: flex; flex-direction: column; display: flex; flex-direction: column;
align-items: center; gap: var(--space-3); align-items: center; gap: var(--space-3);
@ -546,6 +568,7 @@ window.Page_welcome = (() => {
.wc-lhero-cta { flex-direction: row; justify-content: center; } .wc-lhero-cta { flex-direction: row; justify-content: center; }
.wc-btn-hero { width: auto; } .wc-btn-hero { width: auto; }
.wc-grid { grid-template-columns: repeat(4, 1fr); } .wc-grid { grid-template-columns: repeat(4, 1fr); }
.wc-phone-frame { width: 220px; }
} }
@media (min-width: 1024px) { @media (min-width: 1024px) {
.wc-lhero { padding: var(--space-8) var(--space-4) var(--space-10); } .wc-lhero { padding: var(--space-8) var(--space-4) var(--space-10); }
@ -570,18 +593,26 @@ window.Page_welcome = (() => {
const headline = _container.querySelector('#wc-hero-headline'); const headline = _container.querySelector('#wc-hero-headline');
const sub = _container.querySelector('#wc-hero-sub'); const sub = _container.querySelector('#wc-hero-sub');
const counter = _container.querySelector('#wc-hero-counter'); const counter = _container.querySelector('#wc-hero-counter');
const screen = _container.querySelector('#wc-phone-screen');
if (!headline || !sub) return; if (!headline || !sub) return;
// nächste Screenshots vorladen
HERO_SLIDES.forEach(s => { const i = new Image(); i.src = s.screen; });
_heroInterval = setInterval(() => { _heroInterval = setInterval(() => {
headline.style.opacity = '0'; headline.style.opacity = '0';
sub.style.opacity = '0'; sub.style.opacity = '0';
if (screen) screen.style.opacity = '0';
setTimeout(() => { setTimeout(() => {
idx = (idx + 1) % HERO_SLIDES.length; idx = (idx + 1) % HERO_SLIDES.length;
headline.textContent = HERO_SLIDES[idx].headline; const slide = HERO_SLIDES[idx];
sub.textContent = HERO_SLIDES[idx].sub; headline.textContent = slide.headline;
sub.textContent = slide.sub;
if (screen) screen.src = slide.screen;
headline.style.opacity = '1'; headline.style.opacity = '1';
sub.style.opacity = '1'; sub.style.opacity = '1';
if (screen) screen.style.opacity = '1';
if (counter) counter.textContent = `${idx + 1} / ${HERO_SLIDES.length}`; if (counter) counter.textContent = `${idx + 1} / ${HERO_SLIDES.length}`;
}, 400); }, 400);
}, 6000); }, 6000);

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache Offline-Cache + Push Notifications + Tile-Cache
============================================================ */ ============================================================ */
const CACHE_VERSION = 'by-v448'; const CACHE_VERSION = 'by-v449';
const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten