我正在尝试在 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;
}
}
};
您可以使用
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}
/>
);
};