CoolFace
Apppublic

girenit/Argus

sourceHugging Faceotherupdated 1mo agoView on Hugging Face
0likes
app.js129 linesDownload Raw Back to root
1const root = document.documentElement;2root.classList.remove("no-js");3root.classList.add("js");4 5const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)");6const finePointer = window.matchMedia("(pointer: fine)");7const revealItems = [...document.querySelectorAll("[data-reveal]")];8 9function revealAll() {10  revealItems.forEach((item) => item.classList.add("is-visible"));11}12 13if (reduceMotion.matches || !("IntersectionObserver" in window)) {14  revealAll();15} else {16  const revealObserver = new IntersectionObserver(17    (entries, observer) => {18      entries.forEach((entry) => {19        if (!entry.isIntersecting) return;20        entry.target.classList.add("is-visible");21        observer.unobserve(entry.target);22      });23    },24    { rootMargin: "0px 0px -8%", threshold: 0.08 },25  );26  revealItems.forEach((item) => revealObserver.observe(item));27}28 29const scenes = [...document.querySelectorAll("[data-scene]")];30const sceneButtons = [...document.querySelectorAll("[data-scene-button]")];31const storySteps = [...document.querySelectorAll("[data-story-step]")];32let activeScene = "fleet";33let sceneTimer;34 35function setScene(name) {36  const target = scenes.find((scene) => scene.dataset.scene === name);37  if (!target || name === activeScene) return;38 39  window.clearTimeout(sceneTimer);40  const previous = scenes.find((scene) => scene.dataset.scene === activeScene);41  previous?.classList.remove("is-active");42 43  target.hidden = false;44  requestAnimationFrame(() => target.classList.add("is-active"));45 46  sceneTimer = window.setTimeout(() => {47    scenes.forEach((scene) => {48      if (scene !== target) scene.hidden = true;49    });50  }, reduceMotion.matches ? 0 : 520);51 52  activeScene = name;53  sceneButtons.forEach((button) => {54    button.setAttribute("aria-selected", String(button.dataset.sceneButton === name));55  });56  storySteps.forEach((step) => {57    step.classList.toggle("is-active", step.dataset.storyStep === name);58  });59}60 61document.querySelectorAll("[data-scene-button], [data-story-trigger]").forEach((button) => {62  button.addEventListener("click", () => {63    const name = button.dataset.sceneButton || button.dataset.storyTrigger;64    setScene(name);65  });66});67 68if (!reduceMotion.matches && "IntersectionObserver" in window) {69  const storyObserver = new IntersectionObserver(70    (entries) => {71      const visible = entries72        .filter((entry) => entry.isIntersecting)73        .sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0];74      if (visible?.target.dataset.storyStep) setScene(visible.target.dataset.storyStep);75    },76    { rootMargin: "-27% 0px -45%", threshold: [0.15, 0.35, 0.55] },77  );78  storySteps.forEach((step) => storyObserver.observe(step));79}80 81const tiltArea = document.querySelector("[data-tilt]");82const tiltTarget = tiltArea?.querySelector(".hero-console");83 84if (tiltArea && tiltTarget && finePointer.matches && !reduceMotion.matches) {85  tiltArea.addEventListener("pointermove", (event) => {86    const rect = tiltArea.getBoundingClientRect();87    const x = (event.clientX - rect.left) / rect.width - 0.5;88    const y = (event.clientY - rect.top) / rect.height - 0.5;89    tiltTarget.style.transform = `rotateY(${x * 5 - 3}deg) rotateX(${-y * 4 + 1}deg) translate3d(0, -2px, 0)`;90  });91  tiltArea.addEventListener("pointerleave", () => {92    tiltTarget.style.transform = "rotateY(-5deg) rotateX(2deg) translate3d(0, 0, 0)";93  });94}95 96document.querySelectorAll("[data-surface]").forEach((surface) => {97  if (!finePointer.matches || reduceMotion.matches) return;98  surface.addEventListener("pointermove", (event) => {99    const rect = surface.getBoundingClientRect();100    surface.style.setProperty("--pointer-x", `${event.clientX - rect.left}px`);101    surface.style.setProperty("--pointer-y", `${event.clientY - rect.top}px`);102  });103});104 105const header = document.querySelector(".site-header");106let scrollFrame = 0;107 108function updateScrollState() {109  const scrollable = Math.max(document.documentElement.scrollHeight - window.innerHeight, 1);110  root.style.setProperty("--page-progress", Math.min(window.scrollY / scrollable, 1));111  header?.classList.toggle("is-scrolled", window.scrollY > 18);112  scrollFrame = 0;113}114 115window.addEventListener(116  "scroll",117  () => {118    if (scrollFrame) return;119    scrollFrame = requestAnimationFrame(updateScrollState);120  },121  { passive: true },122);123 124reduceMotion.addEventListener("change", () => {125  if (reduceMotion.matches) revealAll();126});127 128updateScrollState();129