src/lib/components/ProgressiveImage.svelte 1.2 K raw
1
<script lang="ts">
2
let {
3
	src,
4
	thumb,
5
	alt,
6
	blurData,
7
	class: className = "",
8
}: {
9
	src: string;
10
	thumb: string;
11
	alt: string;
12
	blurData?: string;
13
	class?: string;
14
} = $props();
15
16
let loaded = $state(false);
17
let thumbAspect = $state(0);
18
let thumbImg: HTMLImageElement;
19
20
function onThumbLoad() {
21
	if (thumbImg.naturalWidth && thumbImg.naturalHeight) {
22
		thumbAspect = thumbImg.naturalWidth / thumbImg.naturalHeight;
23
	}
24
}
25
26
$effect(() => {
27
	loaded = false;
28
	const img = new Image();
29
	img.onload = () => {
30
		loaded = true;
31
	};
32
	img.src = src;
33
34
	return () => {
35
		img.onload = null;
36
	};
37
});
38
39
let placeholderSrc = $derived(blurData || thumb);
40
</script>
41
42
<div
43
  class="progressive-container"
44
  style="max-width: 4000px; {thumbAspect ? `aspect-ratio: ${thumbAspect};` : ''}"
45
>
46
  <img
47
    bind:this={thumbImg}
48
    src={loaded ? src : placeholderSrc}
49
    {alt}
50
    class="{className} progressive-image"
51
    class:progressive-loading={!loaded}
52
    onload={onThumbLoad}
53
  />
54
</div>
55
56
<style>
57
  .progressive-container {
58
    width: 100%;
59
  }
60
61
  .progressive-container .progressive-image {
62
    width: 100%;
63
    height: 100%;
64
    object-fit: contain;
65
  }
66
67
  .progressive-image {
68
    transition: filter 0.6s ease-out;
69
  }
70
71
  .progressive-loading {
72
    filter: blur(20px);
73
  }
74
</style>