Skip to content

React

First, follow the installation instructions to add the NPM package @fancyapps/ui.

Use the usePanzoom hook sample code or create your own wrapper based on it:

js
import { useRef, useState, useCallback, useEffect } from "react";

import { Panzoom } from "@fancyapps/ui/dist/panzoom/";
import "@fancyapps/ui/dist/panzoom/panzoom.css";

import { canUseDOM } from "@fancyapps/ui/dist/utils/canUseDOM.js";
import { isEqual } from "@fancyapps/ui/dist/utils/isEqual.js";

export default function usePanzoom(options = {}) {
  const storedOptions = useRef(options);

  const [container, setContainer] = useState(null);
  const [panzoomInstance, setPanzoomInstance] = useState(undefined);

  const reInit = useCallback(() => {
    if (panzoomInstance) {
      panzoomInstance.destroy().init();
    }
  }, [panzoomInstance]);

  useEffect(() => {
    if (!isEqual(options, storedOptions.current)) {
      storedOptions.current = options;
      reInit();
    }
  }, [options, reInit]);

  useEffect(() => {
    if (canUseDOM() && container) {
      const newPanzoomInstance = Panzoom(
        container,
        storedOptions.current
      ).init();

      setPanzoomInstance(newPanzoomInstance);

      return () => {
        newPanzoomInstance.destroy();
      };
    } else {
      setPanzoomInstance(undefined);
    }
  }, [container]);

  return [setContainer, panzoomInstance];
}
ts
import { useRef, useState, useCallback, useEffect } from "react";

import {
  type PanzoomOptions,
  type PanzoomInstance,
  Panzoom,
} from "@fancyapps/ui/dist/panzoom/";
import "@fancyapps/ui/dist/panzoom/panzoom.css";

import { canUseDOM } from "@fancyapps/ui/dist/utils/canUseDOM.js";
import { isEqual } from "@fancyapps/ui/dist/utils/isEqual.js";

export type PanzoomContainerRefType = <ContainerElement extends HTMLElement>(
  el: ContainerElement | null
) => void;

export type usePanzoom = [PanzoomContainerRefType, PanzoomInstance | undefined];

export default function usePanzoom(
  options: Partial<PanzoomOptions> = {}
): usePanzoom {
  const storedOptions = useRef(options);

  const [container, setContainer] = useState<HTMLElement | null>(null);
  const [panzoomInstance, setPanzoomInstance] = useState<
    PanzoomInstance | undefined
  >(undefined);

  const reInit = useCallback(() => {
    if (panzoomInstance) {
      panzoomInstance.destroy().init();
    }
  }, [panzoomInstance]);

  useEffect(() => {
    if (!isEqual(options, storedOptions.current)) {
      storedOptions.current = options;
      reInit();
    }
  }, [options, reInit]);

  useEffect(() => {
    if (canUseDOM() && container) {
      const newPanzoomInstance = Panzoom(
        container,
        storedOptions.current
      ).init();

      setPanzoomInstance(newPanzoomInstance);

      return () => {
        newPanzoomInstance.destroy();
      };
    } else {
      setPanzoomInstance(undefined);
    }
  }, [container]);

  return [setContainer, panzoomInstance];
}

Usage:

ts
function App() {
  const [panzoomRef] = usePanzoom({
    // Your custom options
  });

  return (
    <div ref={panzoomRef} style={{ height: "400px" }}>
      <img
        className="f-panzoom__content"
        src="https://lipsum.app/id/60/2000x1500"
        width="2000"
        height="1500"
        alt="Sample image"
      />
    </div>
  );
}

Here is a sample of how to access the Panzoom API:

ts
const [panzoomRef, panzoomInstance] = usePanzoom({
  // Your custom options
});

useEffect(() => {
  if (panzoomInstance) {
    // Access API
    console.log(panzoomInstance.getContainer());
  }
}, [panzoomInstance]);