Lib: Remove previous lazy loading approach and replace with static aspect ratios

The element size must be valid before it is loaded, this is a problem
for cards, as they adapt to their content's size.
Previously I tried to load the first card non-lazily and measure its
dimensions for the next cards, but that was not stable on viewport
changes (could have measured the aspect ratio instead...).
Now, all the aspect ratios are just measured and defined manually,
stupid but simple.
This commit is contained in:
2024-12-16 20:06:56 +01:00
parent d398ab67e0
commit f0c568b982
6 changed files with 83 additions and 78 deletions

View File

@ -6,7 +6,12 @@
import type { Driver } from "$lib/schema";
import Input from "./Input.svelte";
import LazyDropdown, { type LazyDropdownOption } from "./LazyDropdown.svelte";
import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config";
import {
DRIVER_CARD_ASPECT_HEIGHT,
DRIVER_CARD_ASPECT_WIDTH,
DRIVER_HEADSHOT_HEIGHT,
DRIVER_HEADSHOT_WIDTH,
} from "$lib/config";
interface DriverCardProps {
/** The [Driver] object used to prefill values. */
@ -43,7 +48,8 @@
</script>
<LazyCard
group="DriverCard"
cardwidth={DRIVER_CARD_ASPECT_WIDTH}
cardheight={DRIVER_CARD_ASPECT_HEIGHT}
imgsrc={driver?.headshot_url ?? headshot_template}
imgwidth={DRIVER_HEADSHOT_WIDTH}
imgheight={DRIVER_HEADSHOT_HEIGHT}

View File

@ -1,12 +1,3 @@
<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";
@ -30,11 +21,11 @@
/** Hide the header image element. It can be shown by removing the "hidden" property using JS and the imgid. */
imghidden?: boolean;
/** Enable to give the card the "w-full" class. */
fullwidth?: boolean;
/** The aspect ratio width used to reserve card space (while its loading) */
cardwidth: number;
/** 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;
/** The aspect ratio height used to reserve card space (while its loading) */
cardheight: number;
}
let {
@ -44,76 +35,46 @@
imgwidth,
imgheight,
imghidden = false,
fullwidth = false,
group,
cardwidth,
cardheight,
...restProps
}: CardProps = $props();
let load: boolean = $state(false);
const lazy_visible_handler = () => {
console.log("Hi");
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>
<!-- 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
id={imgid}
src={imgsrc}
{imgwidth}
{imgheight}
alt="Card header"
draggable="false"
class="select-none shadow"
hidden={imghidden}
/>
{/if}
<div
use:lazyload
onLazyVisible={lazy_visible_handler}
style="width: 100%; aspect-ratio: {cardwidth} / {cardheight};"
>
<div class="card w-full overflow-hidden bg-white shadow">
<!-- 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>
</div>
{/if}
{/if}
</div>
</div>

View File

@ -6,7 +6,12 @@
import type { Race } from "$lib/schema";
import Input from "./Input.svelte";
import { format } from "date-fns";
import { RACE_PICTOGRAM_HEIGHT, RACE_PICTOGRAM_WIDTH } from "$lib/config";
import {
RACE_CARD_ASPECT_HEIGHT,
RACE_CARD_ASPECT_WIDTH,
RACE_PICTOGRAM_HEIGHT,
RACE_PICTOGRAM_WIDTH,
} from "$lib/config";
interface RaceCardProps {
/** The [Race] object used to prefill values. */
@ -56,7 +61,8 @@
</script>
<LazyCard
group="RaceCard"
cardwidth={RACE_CARD_ASPECT_WIDTH}
cardheight={RACE_CARD_ASPECT_HEIGHT}
imgsrc={race?.pictogram_url ?? pictogram_template}
imgwidth={RACE_PICTOGRAM_WIDTH}
imgheight={RACE_PICTOGRAM_HEIGHT}

View File

@ -5,7 +5,12 @@
import { get_by_value } from "$lib/database";
import LazyDropdown, { type LazyDropdownOption } from "./LazyDropdown.svelte";
import type { Action } from "svelte/action";
import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config";
import {
DRIVER_HEADSHOT_HEIGHT,
DRIVER_HEADSHOT_WIDTH,
SUBSTITUTION_CARD_ASPECT_HEIGHT,
SUBSTITUTION_CARD_ASPECT_WIDTH,
} from "$lib/config";
interface SubstitutionCardProps {
/** The [Substitution] object used to prefill values. */
@ -77,7 +82,8 @@
</script>
<LazyCard
group="SubstitutionCard"
cardwidth={SUBSTITUTION_CARD_ASPECT_WIDTH}
cardheight={SUBSTITUTION_CARD_ASPECT_HEIGHT}
imgsrc={get_by_value(drivers, "id", substitution?.substitute ?? "")?.headshot_url ??
headshot_template}
imgwidth={DRIVER_HEADSHOT_WIDTH}

View File

@ -4,7 +4,12 @@
import Button from "./Button.svelte";
import type { Team } from "$lib/schema";
import Input from "./Input.svelte";
import { TEAM_LOGO_HEIGHT, TEAM_LOGO_WIDTH } from "$lib/config";
import {
TEAM_CARD_ASPECT_HEIGHT,
TEAM_CARD_ASPECT_WIDTH,
TEAM_LOGO_HEIGHT,
TEAM_LOGO_WIDTH,
} from "$lib/config";
import LazyCard from "./LazyCard.svelte";
interface TeamCardProps {
@ -30,7 +35,8 @@
</script>
<LazyCard
group="TeamCard"
cardwidth={TEAM_CARD_ASPECT_WIDTH}
cardheight={TEAM_CARD_ASPECT_HEIGHT}
imgsrc={team?.logo_url ?? logo_template}
imgwidth={TEAM_LOGO_WIDTH}
imgheight={TEAM_LOGO_HEIGHT}