fix: hide image during cache resolution to show placeholder

Image is hidden (opacity 0) until the cached URL resolves, preventing
broken image icons. The disc placeholder shows through while loading.
Adds a subtle 150ms fade-in transition when the image appears.
This commit is contained in:
vicrodh committed 2026-03-08 10:57:23 -06:00
1 parent 6b87554dbb
commit 8379227a0e
2 files changed
+14 -5

No files matched your search

+13 -5
View File
@@ -4,8 +4,8 @@
* Usage: * Usage:
* <img use:cachedSrc={imageUrl} alt="..." /> * <img use:cachedSrc={imageUrl} alt="..." />
* *
* Loads the image through the backend cache. Shows nothing until resolved, * Hides the image until the cached URL is resolved, preventing
* then sets the src attribute. Falls back to original URL on error. * broken image icons. The placeholder behind it remains visible.
*/ */
import { getCachedImageUrl } from '$lib/services/imageCacheService'; import { getCachedImageUrl } from '$lib/services/imageCacheService';
@@ -13,21 +13,29 @@ import { getCachedImageUrl } from '$lib/services/imageCacheService';
export function cachedSrc(node: HTMLImageElement, url: string | undefined) { export function cachedSrc(node: HTMLImageElement, url: string | undefined) {
let currentUrl = url; let currentUrl = url;
// Hide until resolved so placeholder shows through
node.style.opacity = '0';
node.removeAttribute('src');
async function resolve(imageUrl: string | undefined) { async function resolve(imageUrl: string | undefined) {
if (!imageUrl) { if (!imageUrl) {
node.src = ''; node.removeAttribute('src');
node.style.opacity = '0';
return; return;
} }
node.style.opacity = '0';
try { try {
const resolved = await getCachedImageUrl(imageUrl); const resolved = await getCachedImageUrl(imageUrl);
// Only apply if still the current URL (avoid race conditions)
if (imageUrl === currentUrl) { if (imageUrl === currentUrl) {
node.src = resolved; node.src = resolved;
node.style.opacity = '1';
} }
} catch { } catch {
if (imageUrl === currentUrl) { if (imageUrl === currentUrl) {
node.src = imageUrl; // Fallback to original node.src = imageUrl;
node.style.opacity = '1';
} }
} }
} }
+1
View File
@@ -361,6 +361,7 @@
object-fit: cover; object-fit: cover;
border-radius: inherit; border-radius: inherit;
z-index: 1; z-index: 1;
transition: opacity 0.15s ease-in;
} }
.artwork-placeholder { .artwork-placeholder {