Component

Canvas 2D

See on GitHub

A 2D canvas component and hook that handles sizing, pixel ratio, and context setup.

'use client'

import { useCallback } from 'react'
import { Canvas2D } from '@/components/canvas-2d'

function Canvas2DDemo() {
  const onContext = useCallback(
    ({
      ctx,
      width,
      height,
    }: {
      ctx: CanvasRenderingContext2D
      width: number
      height: number
    }) => {
      let raf: number

      const primary = getComputedStyle(document.documentElement)
        .getPropertyValue('--primary')
        .trim()

      const cols = 24
      const rows = 16
      const cellW = width / cols
      const cellH = height / rows

      function draw(t: number) {
        ctx.clearRect(0, 0, width, height)

        for (let x = 0; x < cols; x++) {
          for (let y = 0; y < rows; y++) {
            const cx = x * cellW + cellW / 2
            const cy = y * cellH + cellH / 2

            const phase =
              Math.sin(x * 0.3 + t * 0.002) *
                Math.cos(y * 0.3 + t * 0.0015) *
                0.5 +
              0.5

            const radius = phase * Math.min(cellW, cellH) * 0.35 + 1

            ctx.beginPath()
            ctx.arc(cx, cy, radius, 0, Math.PI * 2)
            ctx.fillStyle = `oklch(from ${primary} l c h / ${0.15 + phase * 0.65})`
            ctx.fill()
          }
        }

        raf = requestAnimationFrame(draw)
      }

      raf = requestAnimationFrame(draw)
      return () => cancelAnimationFrame(raf)
    },
    []
  )

  return (
    <Canvas2D
      className="bg-background aspect-video w-full"
      onContext={onContext}
    />
  )
}

export default Canvas2DDemo

Installation

pnpm dlx shadcn@latest add @joyco/canvas-2d

Usage

Component

Canvas2D is a plain <canvas> element that observes its own CSS size and keeps the buffer resolution and DPR in sync.

Return a cleanup function from onContext to cancel animation frames or dispose resources, it's called on unmount and before every re-invocation (resize, DPR change).

import { Canvas2D } from '@/components/canvas-2d'
 
function App() {
  return (
    <Canvas2D
      className="h-96 w-full"
      onContext={({ ctx, width, height, dpr }) => {
        let raf: number
 
        function draw(t: number) {
          ctx.clearRect(0, 0, width, height)
          // ... draw
          raf = requestAnimationFrame(draw)
        }
 
        raf = requestAnimationFrame(draw)
        return () => cancelAnimationFrame(raf)
      }}
    />
  )
}

Hook

For more control, use useCanvas2D directly. It returns a canvas ref callback and a state object.

import { useCanvas2D } from '@/components/canvas-2d'
 
function MyCanvas() {
  const [canvasRef, { ctx, width, height, dpr }] = useCanvas2D({ maxDpr: 2 })
 
  return <canvas ref={canvasRef} className="h-96 w-full" />
}

Props

Canvas2D

PropTypeDefaultDescription
maxDprnumber2Maximum device pixel ratio
onContext(state) => void | (() => void)Called with { ctx, width, height, dpr } when the canvas is ready or resizes. Return a cleanup function to dispose resources.

useCanvas2D

ParamTypeDefaultDescription
maxDprnumber2Maximum device pixel ratio

Related Components

Maintainers
Downloads
0Total
0 downloads today