This commit is contained in:
team3
2026-09-09 10:01:58 +02:00
parent 446edc85fb
commit 2bd03000e8

View File

@@ -1,5 +1,5 @@
<script setup> <script setup>
import { computed, onMounted, onUnmounted, ref, watch } from 'vue' import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { abonnieren } from './live' import { abonnieren } from './live'
import MovieCard from './components/MovieCard.vue' import MovieCard from './components/MovieCard.vue'
import FilterSidebar from './components/FilterSidebar.vue' import FilterSidebar from './components/FilterSidebar.vue'
@@ -512,6 +512,56 @@ const visibleCanon = computed(() =>
filterGruppen(canonGroups.value, search.value.trim().toLowerCase()) filterGruppen(canonGroups.value, search.value.trim().toLowerCase())
) )
// Gefiltert wird über den ganzen Katalog, gerendert nur der Anfang davon.
// Alle 2554 Karten auf einmal sind in der Detailansicht über zehntausend
// Bilder — der Browser meldet dann "Seite reagiert nicht".
const STAPEL = 100
const gezeigt = ref(STAPEL)
const aktuelleListe = computed(() =>
activeTab.value === 'netflix' ? visible.value : visibleCanon.value
)
const sichtbar = computed(() => aktuelleListe.value.slice(0, gezeigt.value))
// Jede neue Suche, jeder Filter und jeder Reiterwechsel fängt oben an
watch([aktuelleListe, ansicht], () => {
gezeigt.value = STAPEL
})
async function mehrLaden() {
if (gezeigt.value >= aktuelleListe.value.length) return
gezeigt.value += STAPEL
// Der Beobachter meldet sich nur beim Wechsel sichtbar/unsichtbar. Bleibt
// der Fuß nach dem Nachladen im Blick — großer Bildschirm, kurze Liste —,
// käme sonst nie ein zweiter Stapel.
await nextTick()
const el = nachschub.value
if (el && el.getBoundingClientRect().top < window.innerHeight + 600) mehrLaden()
}
// Der Fuß der Liste meldet sich, sobald er in Sichtweite kommt. Ein
// Scroll-Handler müsste bei jedem Pixel rechnen, das hier nicht.
// Gleich hier angelegt, nicht in onMounted: die Vorlage setzt die Referenz
// schon beim ersten Zeichnen, da muss der Beobachter bereits stehen.
const nachschub = ref(null)
const beobachter = new IntersectionObserver(
(eintraege) => {
if (eintraege.some((e) => e.isIntersecting)) mehrLaden()
},
// Vorlauf, damit der Nachschub schon da ist, bevor die Liste endet
{ rootMargin: '600px' }
)
onUnmounted(() => beobachter.disconnect())
// Der Fuß verschwindet und kommt wieder, je nach Filter — also bei jedem
// Wechsel neu anmelden
watch(nachschub, (el, vorher) => {
if (vorher) beobachter.unobserve(vorher)
if (el) beobachter.observe(el)
})
// Die Reihe zum geöffneten Film. Im Kanon ist das die Gruppe der Vorlage, // Die Reihe zum geöffneten Film. Im Kanon ist das die Gruppe der Vorlage,
// sonst TMDBs Collection — dort aber nur, was auch auf Netflix liegt. // sonst TMDBs Collection — dort aber nur, was auch auf Netflix liegt.
const series = computed(() => { const series = computed(() => {
@@ -681,10 +731,7 @@ onUnmounted(() => {
</p> </p>
<div class="grid" :class="ansicht"> <div class="grid" :class="ansicht">
<template <template v-for="entry in sichtbar" :key="entry.group">
v-for="entry in activeTab === 'netflix' ? visible : visibleCanon"
:key="entry.group"
>
<MovieCard <MovieCard
v-if="entry.parts.length === 1" v-if="entry.parts.length === 1"
:movie="entry.parts[0]" :movie="entry.parts[0]"
@@ -702,6 +749,10 @@ onUnmounted(() => {
/> />
</template> </template>
</div> </div>
<div v-if="gezeigt < aktuelleListe.length" ref="nachschub" class="nachschub">
Lädt weitere Filme …
</div>
</div> </div>
<FilterSidebar <FilterSidebar
@@ -870,6 +921,13 @@ onUnmounted(() => {
} }
} }
.nachschub {
padding: 2rem 0;
color: var(--muted);
font-size: 0.9rem;
text-align: center;
}
.grid { .grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));