From bf1bb2775b7a16785aa84ad44efd81ae94a554b8 Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Fri, 7 Nov 2025 23:47:30 -0700 Subject: [PATCH] detect all images and add a fade in animation on image loads --- src/assets/css/index.css | 16 ++++++++++++ src/index.tsx | 2 ++ src/setup/imageFadeIn.ts | 55 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 src/setup/imageFadeIn.ts diff --git a/src/assets/css/index.css b/src/assets/css/index.css index 6912bcf4..f345dfcc 100644 --- a/src/assets/css/index.css +++ b/src/assets/css/index.css @@ -427,3 +427,19 @@ input[type="range"].styled-slider.slider-progress::-ms-fill-lower { .google-cast-button:not(.casting) google-cast-launcher { @apply brightness-[500]; } + +/* Image fade-in on load */ +img:not([src=""]) { + opacity: 0; + transition: opacity 0.8s ease-in-out; +} + +/* Fade in when image has loaded class */ +img.loaded { + opacity: 1; +} + +/* For images that are already cached/loaded, show them immediately */ +img[complete]:not([src=""]) { + opacity: 1; +} diff --git a/src/index.tsx b/src/index.tsx index 2f3ecee1..5659d764 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -37,10 +37,12 @@ import { isExtensionActiveCached, } from "./backend/extension/messaging"; import { initializeChromecast } from "./setup/chromecast"; +import { initializeImageFadeIn } from "./setup/imageFadeIn"; import { initializeOldStores } from "./stores/__old/migrations"; // initialize initializeChromecast(); +initializeImageFadeIn(); function LoadingScreen(props: { type: "user" | "lazy" }) { const mapping = { diff --git a/src/setup/imageFadeIn.ts b/src/setup/imageFadeIn.ts new file mode 100644 index 00000000..79e779d9 --- /dev/null +++ b/src/setup/imageFadeIn.ts @@ -0,0 +1,55 @@ +/** + * Global image fade-in handler + * Automatically adds 'loaded' class to images when they finish loading + */ +export function initializeImageFadeIn() { + // Handle images that are already loaded (cached) + const handleExistingImages = () => { + const images = document.querySelectorAll("img:not(.loaded)"); + images.forEach((img) => { + const htmlImg = img as HTMLImageElement; + if (htmlImg.complete && htmlImg.naturalHeight !== 0) { + htmlImg.classList.add("loaded"); + } + }); + }; + + // Handle images that load after DOM is ready + const handleImageLoad = (e: Event) => { + const img = e.target as HTMLImageElement; + if (img.tagName === "IMG") { + img.classList.add("loaded"); + } + }; + + // Use event delegation for all images (including dynamically added ones) + document.addEventListener("load", handleImageLoad, true); + + // Handle existing images on initialization + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", handleExistingImages); + } else { + handleExistingImages(); + } + + // Also check periodically for images that might have loaded + // This handles edge cases where the load event might not fire + const checkInterval = setInterval(() => { + const images = document.querySelectorAll("img:not(.loaded)"); + if (images.length === 0) { + clearInterval(checkInterval); + return; + } + images.forEach((img) => { + const htmlImg = img as HTMLImageElement; + if (htmlImg.complete && htmlImg.naturalHeight !== 0) { + htmlImg.classList.add("loaded"); + } + }); + }, 100); + + // Clean up interval after 10 seconds to avoid memory leaks + setTimeout(() => { + clearInterval(checkInterval); + }, 10000); +}