Der Aufmacher endet an der Leiste, und durch sie sieht man nur noch gedämpft

Vier Dinge, die zusammengehören:

Die Spiegelung ist weg. `backgroundExtensionEffect` füllt die Fläche unter der
Leiste, indem er den Bildrand gespiegelt weiterzeichnet — und je größer die zu
füllende Fläche wurde, desto mehr übernahm der Spiegel das Bild. Auf dem iPhone
legte er sich ohne jede Seitenleiste über den Statusbalken. Jetzt läuft das
Bild selbst weiter, über einen negativen Rand.

Es endet dabei an der **Kante der Leiste**, nicht am Fensterrand. Der
Unterschied sind zwölf Punkte, und die waren deutlich zu sehen: neben der
schwebenden Leiste stand ein heller Streifen Bild, den nichts abdeckte.

Das Seitenverhältnis im Karussell musste weichen. Es legte den Aufmacher auf
die Breite des Inhaltsbereichs fest; weil er nach links unter die Leiste
greift, blieb rechts genau so viel schwarz übrig, wie er links gewann.

Und die Maße, in denen das Hintergrundbild angefordert wird, richten sich jetzt
nach dem Rahmen, in dem es landet. Vorher wurden 900×1200 geholt — hochkant —
und in einem 16:9-Rahmen gezeigt: zu sehen war ein Streifen aus der Mitte, oben
und unten fehlte je ein Drittel.

Dazu: auf schmalen Geräten ist der Aufmacher hochkant. In 16:9 blieben auf
einem iPhone keine zweihundertzwanzig Punkte Höhe, und Schriftzug, Zeile,
Beschreibung und zwei Knöpfe lagen übereinander.

Und ein Schleier unter dem Glas der Leiste. Reines Glas zeigt alles, was
dahinter vorbeizieht, und beim Scrollen zieht die ganze Startseite vorbei — aus
den ruhigen Beschriftungen wurde ein unruhiger Brei. Der Schleier nimmt der
Bewegung die Schärfe, ohne die Fläche zuzumauern.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-13 20:05:07 +02:00
co-authored by Claude Opus 5
parent fbed83b10f
commit d0f732402c
9 changed files with 183 additions and 47 deletions
+19 -16
View File
@@ -63,6 +63,7 @@ struct HeroCarousel: View {
@Environment(\.accessibilityReduceMotion) private var reduceMotion
@Environment(\.flixyAccent) private var accent
@Environment(FlixySession.self) private var session
@Environment(\.horizontalSizeClass) private var sizeClass
private var currentIndex: Int {
items.firstIndex { $0.id == shownID } ?? 0
@@ -84,7 +85,12 @@ struct HeroCarousel: View {
.offset(x: (1 - cover) * (isMovingForward ? Self.slide : -Self.slide))
}
}
.aspectRatio(FlixyAspectRatio.hero, contentMode: .fit)
// **Kein** Seitenverhältnis hier: der Aufmacher bringt sein
// eigenes mit, und eines darüber legte ihn auf die Breite des
// Inhaltsbereichs fest. Weil er nach links unter die Seitenleiste
// greift, blieb dann rechts genau so viel schwarz übrig, wie er
// links gewonnen hatte — auf dem Mac ein Streifen von gut zweihundert
// Punkten.
// **Nicht** beschneiden: der Aufmacher greift bewusst nach links
// über seinen Bereich hinaus, damit sein Bild unter der
// Seitenleiste durchläuft. Ein `.clipped()` schnitt genau das ab —
@@ -161,22 +167,19 @@ struct HeroCarousel: View {
}
private func prefetchNeighbours() {
guard let backend = session.backend else { return }
let next = (currentIndex + 1) % items.count
let item = items[next]
// Dieselben Maße wie in `HeroSection` — eine andere Größe wäre eine
// andere Adresse und der Zwischenspeicher träfe daneben.
let backdrop = backend.artworkURL(
path: item.art ?? item.seriesArt ?? item.thumb,
width: 900,
height: 1200
)
// Der Schriftzug gehört dazu. Fehlt er beim Überblenden, steht dort
// erst der gesetzte Titel und das Logo springt eine Sekunde später
// hinterher — zwei Bewegungen, wo eine sein soll.
let logo = backend.rawImageURL(path: item.clearLogoPath)
guard let backend = session.backend, !items.isEmpty else { return }
let item = items[(currentIndex + 1) % items.count]
let compact = sizeClass == .compact
for url in [backdrop, logo].compactMap(\.self) {
// Hintergrundbild und Schriftzug zusammen: fehlt der Schriftzug beim
// Überblenden, steht dort erst der gesetzte Titel und das Logo springt
// eine Sekunde später hinterher — zwei Bewegungen, wo eine sein soll.
let urls = [
HeroArtwork.backdropURL(for: item, backend: backend, compact: compact),
HeroArtwork.logoURL(for: item, backend: backend),
]
for url in urls.compactMap(\.self) {
Task { _ = await ArtworkLoader.shared.image(for: url) }
}
}