你能帮我用 React 在画布上构建缩放功能吗?

问题描述 投票:0回答:1

我正在尝试在 Wheel 事件上构建缩放功能,但结果并不像我预期的那样特别是关于缩小画布的代码

/* eslint-disable @typescript-eslint/no-non-null-assertion */
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
import { useAppSelector } from "../redux/types/hooks";
import styles from "./style/style.module.css";
import useRepaint from "./utilis/useRepaint";
import useDrawWall from "./utilis/useDrawWall";
import useDrawRoom from "./utilis/useDrawRoom";
import { ToolType } from "./utilis/types/toolType";

const WrapCanvas: React.FC = () => {
  const [zoom, setZoom] = useState(1);
  const [isPanning, setIsPanning] = useState<boolean>(false);
  const [lastPanDimension, setLastPanDimension] = useState<[number, number]>([
    0, 0,
  ]);
  const [isDrawing, setIsDrawing] = useState(false);
  const [ctx, setCTX] = useState<CanvasRenderingContext2D | null>(null);
  const canvas = useRef<HTMLCanvasElement | null>(null);
  const [ref, setRef] = useState<{ x: number; y: number } | null>(null);
  const [lastEndPoint, setLastEndPoint] = useState<{
    x: number;
    y: number;
  } | null>(null);
  const [panningDelta, setPanningDelta] = useState<[number, number]>([0, 0]);
  const toolTYPE = useAppSelector((store) => store.toolReducer.toolName);
  const [canvasDimensionSecond, setCanvasDimensionsecond] = useState<number[]>([
    window.innerWidth,
    window.innerHeight - 50,
  ]);
  const [
    reDrawLayer,
    setRepaintContext,
    setCanvasDimension,
    reDrawGroundLayer,
    canvasDimension,
  ] = useRepaint();
  const [drawWall, getCanvasdrawWallContext] = useDrawWall();
  const [drawRoom, getCanvasdrawRoomContext] = useDrawRoom();
  useEffect(() => {
    const context = canvas.current!.getContext("2d")!;
    setCTX(context);
    getCanvasdrawWallContext(context);
    getCanvasdrawRoomContext(context);
    setRepaintContext(context);
    setCanvasDimension([
      canvas.current!.offsetWidth,
      canvas.current!.offsetHeight,
    ]);
  }, []);
  useEffect(() => {
    reDrawGroundLayer(panningDelta);
    reDrawLayer();
  }, [canvasDimension]);

  useEffect(() => {
    setRef(null);
    setIsDrawing(false);
  }, [toolTYPE]);

  const clickHandler = (event: React.MouseEvent<HTMLCanvasElement>) => {
    const x = event.nativeEvent.offsetX;
    const y = event.nativeEvent.offsetY;
    setIsDrawing(!isDrawing);
    if (toolTYPE === ToolType.ExteriorWALL) {
      if (isDrawing) {
        drawWall(
          5,
          { x: ref!.x - panningDelta[0], y: ref!.y - panningDelta[1] },
          { x: x - panningDelta[0], y: y - panningDelta[1] },
          "ExteriorWALL",
          undefined,
          true
        );
        reDrawLayer();
        setRef(null);
      } else {
        setRef({ x: x, y: y });
      }
    } else if (toolTYPE === ToolType.Room) {
      if (isDrawing) {
        drawRoom(5, { x: ref!.x, y: ref!.y }, { x, y }, undefined, true);
        reDrawLayer();
        setRef(null);
      } else {
        setRef({ x: x, y: y });
      }
    } else if (toolTYPE === ToolType.InteriorWALL) {
      if (isDrawing) {
        drawWall(
          2.5,
          { x: ref!.x, y: ref!.y },
          { x, y },
          "InteriorWALL",
          undefined,
          true
        );
        reDrawLayer();
        setRef(null);
      } else {
        setRef({ x: x, y: y });
      }
    }
  };
  useEffect(() => {
    if (isPanning) {
      ctx?.setTransform(1, 0, 0, 1, 0, 0);
      ctx?.clearRect(0, 0, canvasDimensionSecond[0], canvasDimensionSecond[1]);
      ctx?.transform(1, 0, 0, -1, 0, canvasDimensionSecond[1]);
      ctx?.transform(1, 0, 0, 1, panningDelta[0], -panningDelta[1]); // notice the negative sign here
      ctx?.transform(1, 0, 0, -1, 0, canvasDimensionSecond[1]);
      reDrawGroundLayer(panningDelta);
      reDrawLayer();
    }
  }, [panningDelta]);
  const MouseMoveHandler = (event: React.MouseEvent<HTMLCanvasElement>) => {
    if (isDrawing && lastEndPoint && ref) {
      const context = ctx!;
      context.clearRect(
        -panningDelta[0],
        -panningDelta[1],
        canvas.current!.width,
        canvas.current!.height
      );
      reDrawGroundLayer(panningDelta);
    }

    if (isPanning) {
      const lastX = event.pageX;
      const lastY = event.pageY;
      const deltaX = lastX - lastPanDimension![0];
      const deltaY = lastY - lastPanDimension![1];
      setLastPanDimension([lastX, lastY]);
      setPanningDelta([panningDelta[0] + deltaX, deltaY + panningDelta[1]]);
    }

    if (toolTYPE === ToolType.ExteriorWALL && isDrawing) {
      const x = event.nativeEvent.offsetX;
      const y = event.nativeEvent.offsetY;
      setLastEndPoint({ x: x, y: y });
      drawWall(
        5,
        { x: ref!.x - panningDelta[0], y: ref!.y - panningDelta[1] },
        { x: x - panningDelta[0], y: y - panningDelta[1] },
        "ExteriorWALL"
      );
      reDrawLayer();
    } else if (toolTYPE === ToolType.Room && isDrawing) {
      const x = event.nativeEvent.offsetX;
      const y = event.nativeEvent.offsetY;
      setLastEndPoint({ x: x, y: y });
      drawRoom(5, { x: ref!.x, y: ref!.y }, { x, y }, undefined);
      reDrawLayer();
    } else if (toolTYPE === ToolType.InteriorWALL && isDrawing) {
      const x = event.nativeEvent.offsetX;
      const y = event.nativeEvent.offsetY;
      setLastEndPoint({ x: x, y: y });
      drawWall(2.5, { x: ref!.x, y: ref!.y }, { x, y }, "InteriorWALL");
      reDrawLayer();
    }
  };

  const MouseDownHandler = (event: React.MouseEvent<HTMLCanvasElement>) => {
    if (toolTYPE === ToolType.Hand) {
      setIsDrawing(false);
      setRef(null);

      if (!isPanning) {
        const lastX = event.pageX;
        const lastY = event.pageY;
        setLastPanDimension([lastX, lastY]);
      }
      setIsPanning(true);
      canvas!.current!.style.cursor = "move";
    }
  };

  const MouseUpHandler: React.MouseEventHandler = () => {
    if (isPanning) {
      canvas!.current!.style.cursor = "default";
      setIsPanning(false);
    }
  };

  const zoomHandler= (event:any) => {
    event.preventDefault();
  const cameraOffset = { x: window.innerWidth/2, y: window.innerHeight/2 };

  const rect = canvas.current!.getBoundingClientRect();

  const mouseX =event.nativeEvent.offsetX ;
  const mouseY = event.nativeEvent.offsetY ;

  const step = 0.05;
  const delta = event.deltaY < 0 ? 1 + step : 1 - step;



  setZoom(delta );


  ctx!.scale(zoom, zoom);
  ctx!.translate(
    (mouseX - panningDelta[0]) * (1 - delta),
    (mouseY - panningDelta[1]) * (1 - delta)
  );


  ctx!.clearRect(0, 0, canvasDimensionSecond[0], canvasDimensionSecond[1]);
  ctx!.scale(zoom, zoom);

    console.log("panningDelta",(mouseX));
  reDrawGroundLayer(panningDelta);
  reDrawLayer();
  };
  useLayoutEffect(() => {
    function handleResize() {
      if (ctx) {
        setCanvasDimensionsecond([window.innerWidth, window.innerHeight - 50]);
        setCanvasDimension([window.innerWidth, window.innerHeight - 50]);
        const context = ctx!;
        context.clearRect(0, 0, canvas.current!.width, canvas.current!.height);
      }
    }

    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
    };
  });
  return (
    <div className={styles.canvasWrapper}>
      <canvas
        width={canvasDimensionSecond[0]}
        height={canvasDimensionSecond[1]}
        id="viewer"
        ref={canvas}
        onClick={clickHandler}
        onMouseMove={MouseMoveHandler}
        onMouseDown={MouseDownHandler}
        onMouseUp={MouseUpHandler}
        onWheel = {zoomHandler}
      ></canvas>
    </div>
  );
};

export default WrapCanvas;
re

这是用于 reDrawGroundLayer

 const reDrawGroundLayer = (panningDelta: number[]) => {
    if (canvasDimension) {
      const horizontalGridsNum = Math.floor(canvasDimension[0]);
      const verticalGridsNum = Math.floor(canvasDimension[1]);
      const repaintContext = ctx!;
      repaintContext.lineWidth = 0.5;
      repaintContext.strokeStyle = "#e5e5e5";
      let counter = 0;
      for (let i = 0; i < horizontalGridsNum; i++) {
        repaintContext.beginPath();
        repaintContext.moveTo(0 - panningDelta[0], counter - panningDelta[1]);
        repaintContext.lineTo(
          canvasDimension[0] + counter - panningDelta[0],
          counter - panningDelta[1]
        );
        repaintContext.stroke();
        counter += 50;
      }
      counter = 0;
      for (let i = 0; i < verticalGridsNum; i++) {
        repaintContext.beginPath();
        repaintContext.moveTo(counter - panningDelta[0], 0 - panningDelta[1]);
        repaintContext.lineTo(
          counter - panningDelta[0],
          canvasDimension[1] - panningDelta[1]
        );
        repaintContext.stroke();
        counter += 50;
      }
    }
  };

关于缩小网格被破坏enter image description here我想知道它在画布包装器或reDrawGroundLayer或两者上的错误在哪里?

reactjs html5-canvas
1个回答
0
投票

您可以使用

react-cropper
库做到这一点

这里是文档示例的链接在文档中给出

通过 npm 安装

npm install --save react-cropper

快速示例:-

import React, { useRef } from "react";
import Cropper, { ReactCropperElement } from "react-cropper";
import "cropperjs/dist/cropper.css";

const Demo: React.FC = () => {
  const cropperRef = useRef<ReactCropperElement>(null);
  const onCrop = () => {
    const cropper = cropperRef.current?.cropper;
    console.log(cropper.getCroppedCanvas().toDataURL());
  };

  return (
    <Cropper
      src="https://raw.githubusercontent.com/roadmanfong/react-cropper/master/example/img/child.jpg"
      style={{ height: 400, width: "100%" }}
      // Cropper.js options
      initialAspectRatio={16 / 9}
      guides={false}
      crop={onCrop}
      ref={cropperRef}
    />
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.