applyZoomEffect function exported

Last updated: 2026-03-05T21:04:53.788Z

Metrics

LOC: 101 Complexity: 13 Params: 3

Signature

applyZoomEffect( target: Element | null, config: ZoomEffect, ): : Promise<void>

Summary

Applies the zoom effect focused on the target element.

Architecture violations

View all

  • [warning] max-cyclomatic-complexity: 'applyZoomEffect' has cyclomatic complexity 13 (max 10)
  • [warning] max-lines: 'applyZoomEffect' has 101 lines (max 80)

Source Code

export function applyZoomEffect(
  target: Element | null,
  config: ZoomEffect,
): Promise<void> {
  return new Promise((resolve) => {
    // ── Cancel any previous zoom BEFORE touching the DOM ─────────────────
    // We must cancel first so the old cleanup does NOT overwrite our new
    // transform values when its deferred setTimeout fires.
    if (activeCancelFn) {
      activeCancelFn();
      activeCancelFn = null;
    }

    // Capture this zoom's generation AFTER cancelling the previous one.
    const myGeneration = ++zoomGeneration;

    const scale = config.scale ?? 1.4;
    const duration = config.duration ?? 1200;
    const isIndefinite = duration === 0 || config.duration === Infinity;

    // ── Focal point: always anchor to the LEFT edge of the field ─────────
    // Using left edge (beginning of field) rather than center keeps the
    // cursor / typed text in view and avoids zooming to the page center.
    let vx: number;
    let vy: number;

    if (target) {
      const rect = target.getBoundingClientRect();
      // Left edge, vertical center — "start of the field"
      vx = rect.left;
      vy = rect.top + rect.height / 2;
    } else {
      const cursor = getCursorPosition();
      if (!cursor) {
        resolve();
        return;
      }
      vx = cursor.x;
      vy = cursor.y;
    }

    // transform-origin on <html> uses document coordinates
    // (viewport position + scroll offset).
    const cx = vx + window.scrollX;
    const cy = vy + window.scrollY;

    // ── Apply zoom to <html> ──────────────────────────────────────────────
    // Synchronously reset first (no transition) so previous residual state
    // doesn't bleed into this zoom.
    const root = document.documentElement as HTMLElement;
    root.style.transition = "none";
    root.style.transform = "";
    root.style.transformOrigin = "";
    void root.offsetHeight; // force reflow before re-applying

    root.style.transition =
      "transform 350ms cubic-bezier(0.4,0,0.2,1), transform-origin 0ms";
    root.style.transformOrigin = `${cx}px ${cy}px`;
    root.style.transform = `scale(${scale})`;

    let timer: ReturnType<typeof setTimeout> | null = null;

    const cleanup = () => {
      if (timer) clearTimeout(timer);

      // Guard: if a newer zoom has already started, do NOT touch the DOM.
      if (zoomGeneration !== myGeneration) {
        resolve();
        return;
      }

      root.style.transform = "";

      setTimeout(() => {
        // Double-check generation inside the deferred callback as well.
        if (zoomGeneration !== myGeneration) {
          resolve();
          return;
        }
        root.style.transition = "";
        root.style.transformOrigin = "";
        resolve();
      }, 380);
    };

    const cancel = () => {
      cleanup();
      activeCancelFn = null;
    };

    activeCancelFn = cancel;

    if (isIndefinite) {
      // Safety timeout of 60 s — normally cancelled externally when the
      // fill action completes.
      timer = setTimeout(cleanup, 60_000);
    } else {
      timer = setTimeout(cleanup, duration);
    }
  });
}

Dependencies (Outgoing)

graph LR applyZoomEffect["applyZoomEffect"] ZoomEffect["ZoomEffect"] getCursorPosition["getCursorPosition"] applyZoomEffect -->|uses| ZoomEffect applyZoomEffect -->|uses| getCursorPosition style applyZoomEffect fill:#dbeafe,stroke:#2563eb,stroke-width:2px click applyZoomEffect "252fd536336c2277.html" click ZoomEffect "f90cd7c582f2cc71.html" click getCursorPosition "18c6e5f4ac827d5c.html"
TargetType
ZoomEffect uses
getCursorPosition uses

Impact (Incoming)

graph LR applyZoomEffect["applyZoomEffect"] resolveTarget["resolveTarget"] applyEffect["applyEffect"] resolveTarget -->|uses| applyZoomEffect applyEffect -->|calls| applyZoomEffect style applyZoomEffect fill:#dbeafe,stroke:#2563eb,stroke-width:2px click applyZoomEffect "252fd536336c2277.html" click resolveTarget "cefe589bdbdd3b3b.html" click applyEffect "baf0d9f2a6803bb4.html"
SourceType
resolveTarget uses
applyEffect calls