|
|
import { useCallback, useEffect, useRef } from "react";
const primaryLineColor = "rgb(203,213,245)"; const subLineColor = "rgb(226,231,232)";
export default function GridLayer(props: { width: number; height: number; leftPadding: number; rightPadding: number; topPadding: number; bottomPadding: number; columns: number; rows: number; colCellNum: number; rowCellNum: number; visibility: 'hidden' | 'visible'; }) { const xStartPx = props.leftPadding; const xEndPx = props.width - props.rightPadding; const xStepPx = (props.width - props.leftPadding - props.rightPadding) / props.columns; const xUnitPx = xStepPx / props.colCellNum; const yStartPx = props.topPadding; const yEndPx = props.height - props.bottomPadding; const yStepPx = (props.height - props.topPadding - props.bottomPadding) / props.rows; const yUnitPx = yStepPx / props.rowCellNum;
const canvasRef = useRef<HTMLCanvasElement | null>(null); const drawGrid = useCallback( (ctx: CanvasRenderingContext2D) => { ctx.resetTransform(); ctx.clearRect(0, 0, props.width, props.height);
ctx.beginPath(); ctx.strokeStyle = subLineColor; for (let i = 1; i < props.columns * props.colCellNum; ++i) { if (i % props.colCellNum === 0) continue; ctx.moveTo(xStartPx + xUnitPx * i, yStartPx); ctx.lineTo(xStartPx + xUnitPx * i, yEndPx); } for (let j = 1; j < props.rows * props.rowCellNum; ++j) { if (j % props.rowCellNum === 0) continue; ctx.moveTo(xStartPx, yStartPx + yUnitPx * j); ctx.lineTo(xEndPx, yStartPx + yUnitPx * j); } ctx.stroke()
ctx.beginPath(); ctx.strokeStyle = primaryLineColor; for (let i = 0; i <= props.columns; ++i) { ctx.moveTo(xStartPx + xStepPx * i, yStartPx); ctx.lineTo(xStartPx + xStepPx * i, yEndPx); } for (let j = 0; j <= props.rows; ++j) { ctx.moveTo(xStartPx, yStartPx + yStepPx * j); ctx.lineTo(xEndPx, yStartPx + yStepPx * j); } ctx.stroke()
ctx.beginPath(); ctx.strokeStyle = "#999"; // 偏移原点
const xOffset = (xEndPx - xStartPx) / 2; const yOffset = yStepPx * 2; const yMax = yEndPx - yStartPx - yOffset; ctx.translate(xStartPx + xOffset, yStartPx + yOffset); // 绘制原点交叉线
ctx.moveTo(-xOffset, 0); ctx.lineTo(xOffset, 0); ctx.moveTo(0, -yOffset); ctx.lineTo(0, yMax); ctx.stroke()
// 绘制x轴y轴 单位数值
ctx.beginPath(); ctx.fillStyle = "#333333"; ctx.textAlign = "center"; ctx.font = "normal 14px system"; for (let index = -4; index < 5; index++) { ctx.fillText((index * 10).toString(), xStepPx * index, yMax + 20); } for (let index = -1; index < 5; index++) { ctx.fillText((index * 10).toString(), -xOffset - (index > 0 ? 18 : 14), yStepPx * index + 4); } }, [props.colCellNum, props.columns, props.height, props.rowCellNum, props.rows, props.width, xEndPx, xStartPx, xStepPx, xUnitPx, yEndPx, yStartPx, yStepPx, yUnitPx] );
useEffect(() => { // 获取canvas的2D绘图上下文
const canvas = canvasRef.current; if (!canvas) return;
const context = canvas.getContext("2d"); if (!context) return; // 使用context对象进行绘图
drawGrid(context); }, [drawGrid]);
return ( <div style={{visibility: props.visibility}}> <canvas ref={canvasRef} width={props.width} height={props.height}></canvas> </div> ); }
|