You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
3.3 KiB

5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
  1. import { useCallback, useEffect, useRef } from "react";
  2. const primaryLineColor = "rgb(203,213,245)";
  3. const subLineColor = "rgb(226,231,232)";
  4. export default function GridLayer(props: {
  5. width: number;
  6. height: number;
  7. leftPadding: number;
  8. rightPadding: number;
  9. topPadding: number;
  10. bottomPadding: number;
  11. columns: number;
  12. rows: number;
  13. colCellNum: number;
  14. rowCellNum: number;
  15. visibility: 'hidden' | 'visible';
  16. }) {
  17. const xStartPx = props.leftPadding;
  18. const xEndPx = props.width - props.rightPadding;
  19. const xStepPx = (props.width - props.leftPadding - props.rightPadding) / props.columns;
  20. const xUnitPx = xStepPx / props.colCellNum;
  21. const yStartPx = props.topPadding;
  22. const yEndPx = props.height - props.bottomPadding;
  23. const yStepPx = (props.height - props.topPadding - props.bottomPadding) / props.rows;
  24. const yUnitPx = yStepPx / props.rowCellNum;
  25. const canvasRef = useRef<HTMLCanvasElement | null>(null);
  26. const drawGrid = useCallback(
  27. (ctx: CanvasRenderingContext2D) => {
  28. ctx.resetTransform();
  29. ctx.clearRect(0, 0, props.width, props.height);
  30. ctx.beginPath();
  31. ctx.strokeStyle = subLineColor;
  32. for (let i = 1; i < props.columns * props.colCellNum; ++i) {
  33. if (i % props.colCellNum === 0) continue;
  34. ctx.moveTo(xStartPx + xUnitPx * i, yStartPx);
  35. ctx.lineTo(xStartPx + xUnitPx * i, yEndPx);
  36. }
  37. for (let j = 1; j < props.rows * props.rowCellNum; ++j) {
  38. if (j % props.rowCellNum === 0) continue;
  39. ctx.moveTo(xStartPx, yStartPx + yUnitPx * j);
  40. ctx.lineTo(xEndPx, yStartPx + yUnitPx * j);
  41. }
  42. ctx.stroke()
  43. ctx.beginPath();
  44. ctx.strokeStyle = primaryLineColor;
  45. for (let i = 0; i <= props.columns; ++i) {
  46. ctx.moveTo(xStartPx + xStepPx * i, yStartPx);
  47. ctx.lineTo(xStartPx + xStepPx * i, yEndPx);
  48. }
  49. for (let j = 0; j <= props.rows; ++j) {
  50. ctx.moveTo(xStartPx, yStartPx + yStepPx * j);
  51. ctx.lineTo(xEndPx, yStartPx + yStepPx * j);
  52. }
  53. ctx.stroke()
  54. ctx.beginPath();
  55. ctx.strokeStyle = "#999";
  56. // 偏移原点
  57. const xOffset = (xEndPx - xStartPx) / 2;
  58. const yOffset = yStepPx * 2;
  59. const yMax = yEndPx - yStartPx - yOffset;
  60. ctx.translate(xStartPx + xOffset, yStartPx + yOffset);
  61. // 绘制原点交叉线
  62. ctx.moveTo(-xOffset, 0);
  63. ctx.lineTo(xOffset, 0);
  64. ctx.moveTo(0, -yOffset);
  65. ctx.lineTo(0, yMax);
  66. ctx.stroke()
  67. // 绘制x轴y轴 单位数值
  68. ctx.beginPath();
  69. ctx.fillStyle = "#333333";
  70. ctx.textAlign = "center";
  71. ctx.font = "normal 14px system";
  72. for (let index = -4; index < 5; index++) {
  73. ctx.fillText((index * 10).toString(), xStepPx * index, yMax + 20);
  74. }
  75. for (let index = -1; index < 5; index++) {
  76. ctx.fillText((index * 10).toString(), -xOffset - (index > 0 ? 18 : 14), yStepPx * index + 4);
  77. }
  78. },
  79. [props.colCellNum, props.columns, props.height, props.rowCellNum, props.rows, props.width, xEndPx, xStartPx, xStepPx, xUnitPx, yEndPx, yStartPx, yStepPx, yUnitPx]
  80. );
  81. useEffect(() => {
  82. // 获取canvas的2D绘图上下文
  83. const canvas = canvasRef.current;
  84. if (!canvas) return;
  85. const context = canvas.getContext("2d");
  86. if (!context) return;
  87. // 使用context对象进行绘图
  88. drawGrid(context);
  89. }, [drawGrid]);
  90. return (
  91. <div style={{visibility: props.visibility}}>
  92. <canvas ref={canvasRef} width={props.width} height={props.height}></canvas>
  93. </div>
  94. );
  95. }