Lib: Implement (slightly broken) lazy loading of cards
Issues arise when the viewport size changes
This commit is contained in:
@ -43,6 +43,7 @@
|
||||
</script>
|
||||
|
||||
<LazyCard
|
||||
group="DriverCard"
|
||||
imgsrc={driver?.headshot_url ?? headshot_template}
|
||||
imgwidth={DRIVER_HEADSHOT_WIDTH}
|
||||
imgheight={DRIVER_HEADSHOT_HEIGHT}
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
@ -56,6 +56,7 @@
|
||||
</script>
|
||||
|
||||
<LazyCard
|
||||
group="RaceCard"
|
||||
imgsrc={race?.pictogram_url ?? pictogram_template}
|
||||
imgwidth={RACE_PICTOGRAM_WIDTH}
|
||||
imgheight={RACE_PICTOGRAM_HEIGHT}
|
||||
|
@ -77,6 +77,7 @@
|
||||
</script>
|
||||
|
||||
<LazyCard
|
||||
group="SubstitutionCard"
|
||||
imgsrc={get_by_value(drivers, "id", substitution?.substitute ?? "")?.headshot_url ??
|
||||
headshot_template}
|
||||
imgwidth={DRIVER_HEADSHOT_WIDTH}
|
||||
|
@ -30,6 +30,7 @@
|
||||
</script>
|
||||
|
||||
<LazyCard
|
||||
group="TeamCard"
|
||||
imgsrc={team?.logo_url ?? logo_template}
|
||||
imgwidth={TEAM_LOGO_WIDTH}
|
||||
imgheight={TEAM_LOGO_HEIGHT}
|
||||
|
Reference in New Issue
Block a user