Lib: Implement (slightly broken) lazy loading of cards

Issues arise when the viewport size changes
This commit is contained in:
2024-12-16 19:52:46 +01:00
parent df0402a318
commit d398ab67e0
6 changed files with 61 additions and 5 deletions

View File

@ -43,6 +43,7 @@
</script>
<LazyCard
group="DriverCard"
imgsrc={driver?.headshot_url ?? headshot_template}
imgwidth={DRIVER_HEADSHOT_WIDTH}
imgheight={DRIVER_HEADSHOT_HEIGHT}

View File

@ -1,3 +1,12 @@
<script lang="ts" module>
// The first element of a group of cards (e.g. driver cards or team cards)
// will register its height here, once its fully loaded.
// This height is then used as the height of following components.
// This is necessary, because for lazy loading depending on viewport intersection,
// the elements must have their actual height from the beginning.
let group_heights: { [key: string]: number } = {};
</script>
<script lang="ts">
import type { Snippet } from "svelte";
import LazyImage from "./LazyImage.svelte";
@ -23,6 +32,9 @@
/** Enable to give the card the "w-full" class. */
fullwidth?: boolean;
/** The group this card belongs to (e.g. "driver" or "race"). All cards that have the same contents (more specifically, height) may be assigned to the same group. */
group: string;
}
let {
@ -33,6 +45,7 @@
imgheight,
imghidden = false,
fullwidth = false,
group,
...restProps
}: CardProps = $props();
@ -41,11 +54,50 @@
const lazy_visible_handler = () => {
load = true;
};
const set_group_height = (node: HTMLElement) => {
if (group_heights[group]) return;
group_heights[group] = node.getBoundingClientRect().height;
// console.log(`Set card group hight: ${group}: ${group_heights[group]}px`);
};
</script>
<div use:lazyload onLazyVisible={lazy_visible_handler}>
{#if load}
<div class="card overflow-hidden bg-white shadow {fullwidth ? 'w-full' : 'w-auto'}">
<!-- TODO: This component needs to know its own height, otherwise the intersection observer doesn't work -->
<!-- (all elements are visible at once, so no lazy loading...) -->
<div use:lazyload onLazyVisible={lazy_visible_handler} style="width: 100%;">
{#if group_heights[group]}
<!-- A card has already loaded and determined the height for cards of this group -->
<div
class="card overflow-hidden bg-white shadow {fullwidth ? 'w-full' : 'w-auto'}"
style="height: {group_heights[group]}px;"
>
<!-- Allow empty strings for images that only appear after user action -->
{#if imgsrc !== undefined}
<LazyImage
id={imgid}
src={imgsrc}
{imgwidth}
{imgheight}
alt="Card header"
draggable="false"
class="select-none shadow"
hidden={imghidden}
/>
{/if}
<!-- Only lazyload children, as the image is already lazy (also the image fade would break) -->
{#if load}
<div class="p-2" {...restProps}>
{@render children()}
</div>
{/if}
</div>
{:else}
<!-- This card is the first one to load in the group, so it is not lazy-loaded to determine the height -->
<div
class="card overflow-hidden bg-white shadow {fullwidth ? 'w-full' : 'w-auto'}"
use:set_group_height
>
<!-- Allow empty strings for images that only appear after user action -->
{#if imgsrc !== undefined}
<LazyImage

View File

@ -24,7 +24,7 @@
};
const img_opacity_handler = (node: HTMLElement) => {
setTimeout(() => (node.style.opacity = "1"), 10);
setTimeout(() => (node.style.opacity = "1"), 20);
};
</script>
@ -40,7 +40,7 @@
src={data}
use:img_opacity_handler
class="bg-surface-100 transition-opacity"
style="width: 100%; opacity: 0; transition-duration: 250ms;"
style="width: 100%; opacity: 0; transition-duration: 500ms;"
{...restProps}
/>
{/await}

View File

@ -56,6 +56,7 @@
</script>
<LazyCard
group="RaceCard"
imgsrc={race?.pictogram_url ?? pictogram_template}
imgwidth={RACE_PICTOGRAM_WIDTH}
imgheight={RACE_PICTOGRAM_HEIGHT}

View File

@ -77,6 +77,7 @@
</script>
<LazyCard
group="SubstitutionCard"
imgsrc={get_by_value(drivers, "id", substitution?.substitute ?? "")?.headshot_url ??
headshot_template}
imgwidth={DRIVER_HEADSHOT_WIDTH}

View File

@ -30,6 +30,7 @@
</script>
<LazyCard
group="TeamCard"
imgsrc={team?.logo_url ?? logo_template}
imgwidth={TEAM_LOGO_WIDTH}
imgheight={TEAM_LOGO_HEIGHT}