Skip to content

08 — Canvas

flexium-canvas lets you declare canvas drawings as JSX — primitives like <DrawRect>, <DrawCircle>, <DrawText> map directly to canvas paint calls. Signal updates re-paint only the affected region.

tsx
import { Canvas, DrawRect, DrawCircle, DrawText } from 'flexium-canvas/dom'
import { useMouse, useLoop } from 'flexium-canvas/interactive'
import { use } from 'flexium/core'

export function BouncingBall() {
  const [x, setX] = use(100)
  const [y, setY] = use(100)
  const [vx, setVx] = use(2)
  const [vy, setVy] = use(3)

  useLoop(() => {
    setX(prev => {
      const next = prev + vx
      if (next < 0 || next > 400) setVx(v => -v)
      return next
    })
    setY(prev => {
      const next = prev + vy
      if (next < 0 || next > 300) setVy(v => -v)
      return next
    })
  })

  return (
    <Canvas width={400} height={300} background="#0d1117">
      <DrawCircle cx={x} cy={y} r={20} fill="#3b82f6" />
      <DrawText x={10} y={20} text={`(${x.toFixed(0)}, ${y.toFixed(0)})`} fill="white" />
    </Canvas>
  )
}

Available primitives

  • <DrawRect x y width height fill stroke />
  • <DrawCircle cx cy r fill stroke />
  • <DrawArc cx cy r start end fill stroke />
  • <DrawLine x1 y1 x2 y2 stroke strokeWidth />
  • <DrawPath d fill stroke /> — SVG-style path data
  • <DrawText x y text font fill stroke />

All accept signal values — change a signal and the primitive re-paints.

Interactive hooks

  • useMouse(){ x, y, down } for the canvas
  • useKeyboard(){ key, down } map
  • useLoop(fn) — calls fn on every animation frame, capped to monitor refresh rate
tsx
function DrawingApp() {
  const { x, y, down } = useMouse()
  const [points, setPoints] = use<{x:number,y:number}[]>([])

  // Add a point when mouse pressed
  if (down && points[points.length - 1]?.x !== x) {
    setPoints(p => [...p, { x, y }])
  }

  return (
    <Canvas width={500} height={500}>
      {points.map(p => <DrawCircle cx={p.x} cy={p.y} r={2} fill="white" />)}
    </Canvas>
  )
}

Re-paint granularity

Canvas itself doesn't support partial repaint, but flexium-canvas tracks which primitives reference which signals and only re-orders the affected ones in the paint command list. For a 10,000-particle simulation with 100 moving, only 100 commands re-evaluate per frame.

API surface used

  • <Canvas width height background /> — paint surface
  • <DrawRect> / <DrawCircle> / <DrawArc> / <DrawLine> / <DrawPath> / <DrawText> — primitives
  • useMouse() / useKeyboard() / useLoop() — interactive hooks

Next

Production deploy — vite-plugin-flexium optimize: 'auto'.

Released under the MIT License.