Elements
Canvas
A canvas is a view you draw yourself with the HTML Canvas 2D API: charts, gauges, rings, signatures, custom shapes.
The drawing function only records operations; the platform replays them natively (Core Graphics on iOS and Mac
Catalyst, android.graphics.Canvas on Android) at the screen’s resolution.
import { mount } from "nsh";
import { Canvas } from "nsh/canvas";
function Ring(props: { progress: number }) {
return (
<Canvas
style={{ width: 120, height: 120 }}
draw={(ctx, { width, height }) => {
const cx = width / 2, cy = height / 2, r = width / 2 - 10;
ctx.lineWidth = 12;
ctx.lineCap = "round";
ctx.strokeStyle = "#e5e7eb";
ctx.beginPath();
ctx.arc(cx, cy, r, 0, Math.PI * 2);
ctx.stroke();
ctx.strokeStyle = "#6366f1";
ctx.beginPath();
ctx.arc(cx, cy, r, -Math.PI / 2, -Math.PI / 2 + props.progress * Math.PI * 2);
ctx.stroke();
}}
/>
);
}
mount(() => <Ring progress={0.7} />);
Canvas comes from nsh/canvas. It creates the native canvas view, measures it, and runs
your draw function with a context and the canvas’s size.
#Props
Canvas takes the props of a view (style, class, press and gesture props, onLayout, accessibility,
ref, …) plus:
| Prop | Type | Description |
|---|---|---|
draw | (ctx: CanvasRenderingContext2D, size: { width: number; height: number }) => void | Draws the canvas. Runs once the size is known, again when the canvas resizes, and again when signals it reads change. |
Give the canvas a size through its style (width / height, flex, aspectRatio): it has no content size of its own.
#When it redraws
draw is tracked like an effect: reading a signal inside it subscribes the drawing to that signal.
const values = signal([12, 40, 28, 64]);
<Canvas
style={{ height: 160 }}
draw={(ctx, { width, height }) => {
const data = values(); // a change of values() redraws
const step = width / data.length;
ctx.fillStyle = "#0969da";
data.forEach((v, i) => ctx.fillRect(i * step + 4, height - v, step - 8, v));
}}
/>;
Numbers and colors may also be native animated values (CanvasNumber,
CanvasPaint): the platform then redraws on its own while they animate, without running draw or any JS per frame.
import { Animated, createNativeValue } from "nsh/animated";
import { Canvas } from "nsh/canvas";
const angle = createNativeValue(0);
Animated.timing(angle, { toValue: Math.PI * 2, duration: 1000 }).start();
<Canvas
style={{ width: 80, height: 80 }}
draw={(ctx) => {
ctx.translate(40, 40);
ctx.rotate(angle); // animates natively
ctx.fillStyle = "#cf222e";
ctx.fillRect(-20, -4, 40, 8);
}}
/>;
#The context
CanvasRenderingContext2D is a subset of the browser’s: the state stack (save, restore), transforms, paths
(moveTo, lineTo, arc, arcTo, ellipse, rect, roundRect, Bézier curves), fill / stroke / clip (also of
a Path2D, which accepts SVG path data), rectangles, text (fillText, strokeText, measureText), linear / radial /
conic gradients, line dashes, shadows, globalAlpha, globalCompositeOperation and drawImage (images by URL or
imported asset). Every member is listed in the nsh/canvas reference; the
canvas guide walks through drawing charts and animated graphics.
Coordinates are in points with the origin at the top left. measureText is synchronous and returns only width.
Not supported: getImageData / putImageData, filter, isPointInPath and SVG images.
#Events
The view events: onPress, onLayout, gestures and touches. To handle drawing-relative taps, read
locationX / locationY in onTouchEnd, or track a drag with onResponderMove.
#Ref methods
None of its own.