Skip to content
CanvasWrapper.tsx 3.25 KiB
Newer Older
Grant's avatar
Grant committed
import { createRef, useContext, useEffect } from "react";
Grant's avatar
Grant committed
import { Canvas } from "../lib/canvas";
import { useAppContext } from "../contexts/AppContext";
import { PanZoomWrapper } from "@sc07-canvas/lib/src/renderer";
Grant's avatar
Grant committed
import { RendererContext } from "@sc07-canvas/lib/src/renderer/RendererContext";
Grant's avatar
Grant committed
import { ViewportMoveEvent } from "@sc07-canvas/lib/src/renderer/PanZoom";
import throttle from "lodash.throttle";
Grant's avatar
Grant committed
import { IPosition } from "@sc07-canvas/lib/src/net";
import { Template } from "./Template";
Grant's avatar
Grant committed
import { IRouterData, Router } from "../lib/router";
Grant's avatar
Grant committed

export const CanvasWrapper = () => {
  const { config } = useAppContext();
Grant's avatar
Grant committed
  // to prevent safari from blurring things, use the zoom css property
Grant's avatar
Grant committed
  return (
    <main>
      <PanZoomWrapper>
        <CanvasInner />
      </PanZoomWrapper>
Grant's avatar
Grant committed
    </main>
  );
};

const CanvasInner = () => {
  const canvasRef = createRef<HTMLCanvasElement>();
Grant's avatar
Grant committed
  const { config, setCanvasPosition, setCursorPosition } = useAppContext();
Grant's avatar
Grant committed
  const PanZoom = useContext(RendererContext);
Grant's avatar
Grant committed

Grant's avatar
Grant committed
  useEffect(() => {
    Router.PanZoom = PanZoom;
  }, [PanZoom]);

Grant's avatar
Grant committed
  useEffect(() => {
    if (!config?.canvas || !canvasRef.current) return;
Grant's avatar
Grant committed
    const canvas = canvasRef.current!;
Grant's avatar
Grant committed
    const canvasInstance = new Canvas(config, canvas, PanZoom);
Grant's avatar
Grant committed
    const initAt = Date.now();

    const handleNavigate = (data: IRouterData) => {
      if (data.canvas) {
        const position = canvasInstance.canvasToPanZoomTransform(
          data.canvas.x,
          data.canvas.y
        );

        PanZoom.setPosition(
          {
            x: position.transformX,
            y: position.transformY,
            zoom: data.canvas.zoom || 0, // TODO: fit canvas to viewport instead of defaulting
          },
          { suppressEmit: true }
        );
Grant's avatar
Grant committed
    };
Grant's avatar
Grant committed

Grant's avatar
Grant committed
    // initial load
    const initialRouter = Router.get();
    console.log(
      "[CanvasWrapper] Initial router data, handling navigate",
      initialRouter
    );
    handleNavigate(initialRouter);

    const handleViewportMove = (state: ViewportMoveEvent) => {
      if (Date.now() - initAt < 60 * 1000) {
        console.debug(
          "[CanvasWrapper] handleViewportMove called soon after init",
          Date.now() - initAt
        );
      }
Grant's avatar
Grant committed

Grant's avatar
Grant committed
      Router.queueUpdate();
    };
Grant's avatar
Grant committed

    const handleCursorPos = throttle((pos: IPosition) => {
Grant's avatar
Grant committed
      if (
        pos.x < 0 ||
        pos.y < 0 ||
        pos.x > config.canvas.size[0] ||
        pos.y > config.canvas.size[1]
      ) {
        setCursorPosition();
      } else {
        // fixes not passing the current value
        setCursorPosition({ ...pos });
Grant's avatar
Grant committed
      }
Grant's avatar
Grant committed

Grant's avatar
Grant committed
    PanZoom.addListener("viewportMove", handleViewportMove);
Grant's avatar
Grant committed
    canvasInstance.on("cursorPos", handleCursorPos);
Grant's avatar
Grant committed
    Router.on("navigate", handleNavigate);
Grant's avatar
Grant committed

Grant's avatar
Grant committed
    return () => {
      canvasInstance.destroy();
Grant's avatar
Grant committed
      PanZoom.removeListener("viewportMove", handleViewportMove);
Grant's avatar
Grant committed
      canvasInstance.off("cursorPos", handleCursorPos);
Grant's avatar
Grant committed
      Router.off("navigate", handleNavigate);
Grant's avatar
Grant committed
    };
Grant's avatar
Grant committed
    // ! do not include canvasRef, it causes infinite re-renders
  }, [PanZoom, config, setCanvasPosition, setCursorPosition]);
Grant's avatar
Grant committed

  return (
    <canvas
      id="board"
      width="1000"
      height="1000"
      className="pixelate"
      ref={canvasRef}
    ></canvas>
  );
};