fix(immersive): fallback to CSS blur when canvas fails (CORS)

This commit is contained in:
vicrodh committed 2026-01-30 16:18:17 -06:00
1 parent 24a09a88ab
commit f744615b0e
1 file changed
+47 -21
@@ -8,7 +8,8 @@
let { artwork, mode = 'blur' }: Props = $props(); let { artwork, mode = 'blur' }: Props = $props();
let backgroundStyle = $state(''); let backgroundUrl = $state('');
let useCanvasBlur = $state(false);
let isLoading = $state(true); let isLoading = $state(true);
let currentArtwork = $state(''); let currentArtwork = $state('');
@@ -18,23 +19,27 @@
currentArtwork = artwork; currentArtwork = artwork;
isLoading = true; isLoading = true;
useCanvasBlur = false;
if (mode === 'blur') { if (mode === 'blur') {
// Try canvas blur first (most efficient)
generateBlurredBackground(artwork) generateBlurredBackground(artwork)
.then((dataUrl) => { .then((dataUrl) => {
backgroundStyle = `url(${dataUrl})`; backgroundUrl = dataUrl;
useCanvasBlur = true;
isLoading = false; isLoading = false;
}) })
.catch((err) => { .catch((err) => {
console.error('[ImmersiveBackground] Blur generation failed:', err); // CORS error - fallback to CSS blur (works but uses more GPU)
// Fallback to gradient mode console.warn('[ImmersiveBackground] Canvas blur failed (CORS), using CSS blur fallback');
fallbackToGradient(); backgroundUrl = artwork;
useCanvasBlur = false;
isLoading = false;
}); });
} else if (mode === 'gradient') { } else if (mode === 'gradient') {
fallbackToGradient(); fallbackToGradient();
} else { } else {
// Solid dark background backgroundUrl = '';
backgroundStyle = 'var(--bg-primary)';
isLoading = false; isLoading = false;
} }
}); });
@@ -42,20 +47,33 @@
async function fallbackToGradient() { async function fallbackToGradient() {
try { try {
const colors = await extractDominantColors(artwork); const colors = await extractDominantColors(artwork);
backgroundStyle = createGradientFromColors(colors); backgroundUrl = createGradientFromColors(colors);
useCanvasBlur = true; // Gradient doesn't need CSS blur
} catch { } catch {
backgroundStyle = 'linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)'; backgroundUrl = 'linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)';
useCanvasBlur = true;
} }
isLoading = false; isLoading = false;
} }
</script> </script>
<div class="immersive-background" class:loading={isLoading}> <div class="immersive-background" class:loading={isLoading}>
<!-- Pre-computed blur image or gradient --> <!-- Background: either pre-blurred canvas or CSS blur fallback -->
<div {#if useCanvasBlur}
class="background-image" <!-- Pre-computed blur (efficient) -->
style="background: {backgroundStyle};" <div
></div> class="background-image canvas-blur"
style="background-image: url({backgroundUrl});"
></div>
{:else if backgroundUrl}
<!-- CSS blur fallback (works with CORS-restricted images) -->
<img
src={backgroundUrl}
alt=""
class="background-image css-blur"
aria-hidden="true"
/>
{/if}
<!-- Overlay for consistent darkness and vignette --> <!-- Overlay for consistent darkness and vignette -->
<div class="background-overlay"></div> <div class="background-overlay"></div>
@@ -71,16 +89,27 @@
.background-image { .background-image {
position: absolute; position: absolute;
inset: -20px; /* Slight overflow to avoid edge artifacts */ inset: -40px; /* Overflow to avoid blur edge artifacts */
transition: opacity 300ms ease-out;
}
/* Canvas pre-blurred version (efficient - no GPU filter) */
.background-image.canvas-blur {
background-size: cover; background-size: cover;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
transition: background 300ms ease-out;
/* Scale up the tiny blurred image */
transform: scale(1.1); transform: scale(1.1);
} }
/* CSS blur fallback for CORS-restricted images */
.background-image.css-blur {
width: calc(100% + 80px);
height: calc(100% + 80px);
object-fit: cover;
filter: blur(60px) saturate(1.2) brightness(0.5);
transform: scale(1.2);
}
.loading .background-image { .loading .background-image {
opacity: 0; opacity: 0;
} }
@@ -94,7 +123,4 @@
radial-gradient(ellipse at center, transparent 0%, rgba(0, 0, 0, 0.3) 100%), radial-gradient(ellipse at center, transparent 0%, rgba(0, 0, 0, 0.3) 100%),
linear-gradient(to bottom, rgba(0, 0, 0, 0.2) 0%, transparent 30%, transparent 70%, rgba(0, 0, 0, 0.4) 100%); linear-gradient(to bottom, rgba(0, 0, 0, 0.2) 0%, transparent 30%, transparent 70%, rgba(0, 0, 0, 0.4) 100%);
} }
/* No blur filter here - the image is pre-blurred! */
/* This is the key performance optimization */
</style> </style>