diff --git a/src/App.svelte b/src/App.svelte index 47f7ead..6cfa39f 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -117,6 +117,31 @@ }) onMount(() => { + // --- Global image reveal: hide until loaded, then fade in --- + const onImgLoad = (e) => { + if (e.target.tagName === 'IMG') e.target.classList.add('loaded') + } + document.addEventListener('load', onImgLoad, true) + + // MutationObserver: catch cached images before first paint + const imgObserver = new MutationObserver((mutations) => { + for (const m of mutations) { + for (const node of m.addedNodes) { + if (node.nodeType !== 1) continue + const imgs = node.tagName === 'IMG' ? [node] : node.querySelectorAll?.('img') ?? [] + for (const img of imgs) { + if (img.complete && img.naturalWidth > 0) img.classList.add('loaded') + } + } + } + }) + imgObserver.observe(document.body, { childList: true, subtree: true }) + + // Already-present images + document.querySelectorAll('img').forEach(img => { + if (img.complete && img.naturalWidth > 0) img.classList.add('loaded') + }) + const handleResize = () => { isResizing = true clearTimeout(resizeTimer) @@ -181,6 +206,8 @@ window.addEventListener('touchend', handleTouchEnd, { passive: true }) return () => { + document.removeEventListener('load', onImgLoad, true) + imgObserver.disconnect() window.removeEventListener('resize', handleResize) window.removeEventListener('keydown', handleKeydown) window.removeEventListener('touchstart', handleTouchStart) @@ -255,6 +282,12 @@ :global(img) { max-width: 100%; height: auto; + opacity: 0; + transition: opacity 0.3s ease; + } + + :global(img.loaded) { + opacity: 1; } .main {