index.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import type { FC } from 'react'
  2. import { useCallback, useEffect, useRef } from 'react'
  3. type GridMaskProps = {
  4. children: React.ReactNode
  5. wrapperClassName?: string
  6. canvasClassName?: string
  7. gradientClassName?: string
  8. }
  9. const GridMask: FC<GridMaskProps> = ({
  10. children,
  11. wrapperClassName,
  12. canvasClassName,
  13. gradientClassName,
  14. }) => {
  15. const canvasRef = useRef<HTMLCanvasElement | null>(null)
  16. const ctxRef = useRef<CanvasRenderingContext2D | null>(null)
  17. const initCanvas = () => {
  18. const dpr = window.devicePixelRatio || 1
  19. if (canvasRef.current) {
  20. const { width: cssWidth, height: cssHeight } = canvasRef.current?.getBoundingClientRect()
  21. canvasRef.current.width = dpr * cssWidth
  22. canvasRef.current.height = dpr * cssHeight
  23. const ctx = canvasRef.current.getContext('2d')
  24. if (ctx) {
  25. ctx.scale(dpr, dpr)
  26. ctx.strokeStyle = '#D1E0FF'
  27. ctxRef.current = ctx
  28. }
  29. }
  30. }
  31. const drawRecord = useCallback(() => {
  32. const canvas = canvasRef.current!
  33. const ctx = ctxRef.current!
  34. const rowNumber = parseInt(`${canvas.width / 24}`)
  35. const colNumber = parseInt(`${canvas.height / 24}`)
  36. ctx.clearRect(0, 0, canvas.width, canvas.height)
  37. ctx.beginPath()
  38. for (let i = 0; i < rowNumber; i++) {
  39. for (let j = 0; j < colNumber; j++) {
  40. const x = i * 24
  41. const y = j * 24
  42. if (j === 0) {
  43. ctx.moveTo(x, y + 2)
  44. ctx.arc(x + 2, y + 2, 2, Math.PI, Math.PI * 1.5)
  45. ctx.lineTo(x + 22, y)
  46. ctx.arc(x + 22, y + 2, 2, Math.PI * 1.5, Math.PI * 2)
  47. ctx.lineTo(x + 24, y + 22)
  48. ctx.arc(x + 22, y + 22, 2, 0, Math.PI * 0.5)
  49. ctx.lineTo(x + 2, y + 24)
  50. ctx.arc(x + 2, y + 22, 2, Math.PI * 0.5, Math.PI)
  51. }
  52. else {
  53. ctx.moveTo(x + 2, y)
  54. ctx.arc(x + 2, y + 2, 2, Math.PI * 1.5, Math.PI, true)
  55. ctx.lineTo(x, y + 22)
  56. ctx.arc(x + 2, y + 22, 2, Math.PI, Math.PI * 0.5, true)
  57. ctx.lineTo(x + 22, y + 24)
  58. ctx.arc(x + 22, y + 22, 2, Math.PI * 0.5, 0, true)
  59. ctx.lineTo(x + 24, y + 2)
  60. ctx.arc(x + 22, y + 2, 2, 0, Math.PI * 1.5, true)
  61. }
  62. }
  63. }
  64. ctx.stroke()
  65. ctx.closePath()
  66. }, [])
  67. const handleStartDraw = () => {
  68. if (canvasRef.current && ctxRef.current)
  69. drawRecord()
  70. }
  71. useEffect(() => {
  72. initCanvas()
  73. handleStartDraw()
  74. }, [])
  75. return (
  76. <div className={`relative bg-white ${wrapperClassName}`}>
  77. <canvas ref={canvasRef} className={`absolute inset-0 w-full h-full ${canvasClassName}`} />
  78. <div className={`absolute w-full h-full z-[1] bg-gradient-to-b from-white/80 to-white rounded-lg ${gradientClassName}`} />
  79. <div className='relative z-[2]'>{children}</div>
  80. </div>
  81. )
  82. }
  83. export default GridMask