Web Design
loading

Loading Component 設定 class 為 loading

先把 body 或 container 設定成overflow: hidden;(hide scrollbar)

動畫結束後

Enable scroll

Body or Container
document.body.style.overflow = "visible";
 
// or
 
document.getElementById("customIdForThatDiv").style.overflow = "visible";

Hide Loading Component

沒有用 !important 的話可能會遇到 CSS Specificity (opens in a new tab)判斷蓋不過去。

.hidden {
  display: none !important;
}
 
.hidden {
  z-index: -1 !important;
}

要用 delay 確保動畫的播放時間,才不會讀取很快結束很違和。

Loading Component
document.querySelector(".loading").classList.toggle("hidden");
document.querySelector(".loading").classList.add("hidden");
 
// Next.js
useIsomorphicLayoutEffect(() => {
  const ctx = gsap.context(() => {
    if (loaded) {
      gsap.to(".loading", {
        opacity: -0.5,
        delay: 2.5,
        duration,
      });
      gsap.to(".loading-wrap", {
        opacity: -0.5,
        delay: 2.5,
        duration,
        onComplete: () => {
          document.querySelector(".loading-wrap")!.classList.toggle("hidden");
        },
      });
    }
  });
 
  return () => {
    ctx.revert();
  };
}, [loaded]);

Preload Image Hook

usePreloadImage.ts
import { useEffect, useState } from "react";
 
function preloadImage(src: string) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = function () {
      resolve(img);
    };
    img.onerror = img.onabort = function () {
      reject(src);
    };
    img.src = src;
  });
}
 
export default function useImagePreloader(imageList: string[]) {
  const [imagesPreloaded, setImagesPreloaded] = useState<boolean>(false);
 
  useEffect(() => {
    let isCancelled = false;
 
    async function effect() {
      if (isCancelled) {
        return;
      }
 
      const imagesPromiseList: Promise<any>[] = [];
      for (const i of imageList) {
        imagesPromiseList.push(preloadImage(i));
      }
 
      await Promise.all(imagesPromiseList);
 
      if (isCancelled) {
        return;
      }
 
      setImagesPreloaded(true);
    }
 
    effect();
 
    return () => {
      isCancelled = true;
    };
  }, [imageList]);
 
  return { imagesPreloaded };
}