一个基于 TypeScript 和 React 构建的轻量级、高性能 Canvas 渲染引擎。它提供了一套声明式的 API,使用标准的 React 组件来构建复杂的 Canvas 场景。
- 声明式 API: 使用
<Rect>,<Circle>,<Group>等 React 组件来组合场景。 - React Reconciler: 自定义 React 渲染器,将 Fiber 树直接映射为 Canvas 场景图,跳过 DOM Diff 以获得极致性能。
- 场景图: 内置分层对象管理(父子关系)。
- 交互支持: 支持点击、悬停和拖拽事件(
onClick,onMouseEnter,onDragStart等)。 - 智能渲染: 支持 Z-Index 排序、分组裁剪(Clipping),以及使用
requestAnimationFrame优化的渲染循环。 - 高 DPI 支持: 自动处理 Retina/高密度显示屏。
- TypeScript: 使用 TypeScript 编写,提供完整的类型定义。
- 动画 Hooks: 提供
useFramehook 用于实现平滑的逐帧动画。
- 克隆仓库。
- 安装依赖:
npm install
- 启动开发服务器:
npm run dev
canvas-react 提供了一系列内置的图形组件。所有组件都支持通用的属性,如 x, y, fill, stroke, lineWidth, opacity, rotation, scaleX, scaleY, 以及 onClick 等事件监听。
| 组件名 | 关键属性 (Key Props) | 描述 |
|---|---|---|
<Rect /> |
width, height, cornerRadius |
矩形,支持圆角。 |
<Circle /> |
radius |
圆形。 |
<Ellipse /> |
radiusX, radiusY |
椭圆。 |
<Line /> |
points, closed |
折线。设置 closed 后可作为多边形。 |
<Path /> |
data |
根据 SVG Path 数据渲染(例如:M 10 10 L 90 90)。 |
<Text /> |
text, fontSize, align, width, wordWrap |
多行文本,支持自动换行和对齐方式。 |
<Image /> |
image, width, height |
渲染 HTMLImageElement 图像。 |
<Arc /> |
innerRadius, outerRadius, angle, clockwise |
扇形、圆环或圆弧。 |
<RegularPolygon /> |
sides, radius |
正多边形(如三角形、六边形等)。 |
<Star /> |
numPoints, innerRadius, outerRadius |
星形,支持自定义角数和内外半径。 |
<Group /> |
clip, clipWidth, clipHeight |
容器组件,用于对图形进行分组和裁剪。 |
<Transformer /> |
nodes |
用于旋转、缩放其他图形的特殊交互组件。 |
引擎支持矩形、圆形、椭圆、多边形、星形、扇形、文本、线条、Path (SVG) 和图片等丰富的基础图形。所有图形均支持 fill (填充) 和 stroke (描边) 属性。
import Canvas from './src/react/Canvas';
import { Rect, Circle, Ellipse, RegularPolygon, Star, Arc, Text, Line, Image, Path } from './src/react/Shapes';
const App = () => (
<Canvas width={800} height={600}>
{/* 基础矩形 (支持圆角和虚线边框) */}
<Rect x={10} y={10} width={100} height={50} fill="red" stroke="black" lineWidth={2} cornerRadius={10} lineDash={[5, 5]} />
{/* 圆形 */}
<Circle x={200} y={100} radius={30} fill="blue" />
{/* 椭圆 */}
<Ellipse x={300} y={100} radiusX={40} radiusY={20} fill="green" />
{/* 正多边形 */}
<RegularPolygon x={400} y={100} sides={6} radius={30} fill="purple" />
{/* 星形 */}
<Star x={500} y={100} numPoints={5} innerRadius={15} outerRadius={30} fill="yellow" stroke="orange" />
{/* 扇形 / 圆弧 */}
<Arc x={600} y={100} innerRadius={10} outerRadius={30} angle={Math.PI} fill="cyan" />
{/* 文本 (支持换行和垂直对齐) */}
<Text text="你好世界\n第二行" x={10} y={100} fontSize={24} fill="#333" verticalAlign="middle" />
{/* SVG 路径 */}
<Path
data="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z"
fill="pink"
stroke="red"
/>
</Canvas>
);引擎支持所有图形的鼠标和拖拽事件。你也可以在 <Canvas> 上开启 interactive={true} 允许全局的滚轮缩放和拖拽平移画布。
<Canvas width={800} height={600} interactive={true}>
<Rect
x={100}
y={100}
width={50}
height={50}
fill="orange"
onClick={(e) => console.log('点击!', e)}
onMouseEnter={() => console.log('鼠标移入')}
onMouseLeave={() => console.log('鼠标移出')}
draggable={true}
onDragStart={() => console.log('拖拽开始')}
onDragMove={(e) => console.log('拖拽中...', e)}
onDragEnd={() => console.log('拖拽结束')}
/>
</Canvas>为了方便调试复杂的 Canvas 场景,引擎内置了多层次的调试支持。只需在 <Canvas> 上开启 debug={true} 即可启用:
- React DevTools 集成: 开启调试后,你可以直接在 Chrome 的 React DevTools "Components" 面板中查看所有图形节点(如
Rect,Circle)的实时坐标和属性。 - 全局变量暴露: 开启调试后,引擎会将根场景图实例暴露在
window.__CANVAS_STAGE__,你可以在 Chrome 控制台 (Console) 中直接打印和操作它(例如__CANVAS_STAGE__.children)。 - 内置可视化面板: 页面右下角会渲染一个内置的开发者工具面板,展示当前的 FPS、节点树以及边界框高亮功能。
<Canvas width={800} height={600} debug={true}>
{/* ... */}
</Canvas>使用 <Group> 来组织图形。你还可以使用 clip 属性将渲染限制在分组的边界框内,或使用 zIndex 控制渲染顺序。
import { Group, Rect } from './src/react/Shapes';
const Scene = () => (
<Group x={400} y={300} clip={true} clipWidth={200} clipHeight={200}>
{/* 背景 (z-index 0) */}
<Rect width={200} height={200} fill="#eee" zIndex={0} />
{/* 前景 (z-index 10) - 将渲染在最上方 */}
<Rect x={50} y={50} width={100} height={100} fill="red" zIndex={10} />
</Group>
);除了基础的 useFrame,引擎现在内置了补间动画 (Tweening) 系统。你可以直接对任何场景节点使用声明式动画。
import { useEffect, useRef } from 'react';
import { Rect } from './src/react/Shapes';
import { Node } from './src/core/Node';
const AnimatedBox = () => {
const nodeRef = useRef<Node>(null);
useEffect(() => {
if (nodeRef.current) {
// 在 1 秒内平滑过渡到指定属性
nodeRef.current.to({
x: 300,
rotation: Math.PI,
opacity: 0.5,
duration: 1,
easing: (t) => t * (2 - t) // 可选的缓动函数
});
}
}, []);
return <Rect ref={nodeRef} width={100} height={100} fill="purple" />;
};引擎支持将画布或任意节点导出为图片或 JSON。
// 导出为图片 (DataURL)
const dataUrl = canvasRef.current.toDataURL({ mimeType: 'image/png', quality: 1, pixelRatio: 2 });
// 序列化场景图
const json = nodeRef.current.toJSON();场景的根容器。
width: number (默认: 500)height: number (默认: 500)style: CSSPropertiesinteractive: boolean (默认: false) - 开启后支持鼠标拖拽画布平移和滚轮缩放onClick,onDoubleClick,onMouseDown,onMouseUp,onMouseMove,onMouseLeave,onWheel: 全局事件处理器- 方法:
toDataURL(options)导出画布图片
x: number (默认: 0)y: number (默认: 0)rotation: number (弧度, 默认: 0)scaleX: number (默认: 1)scaleY: number (默认: 1)opacity: number (全局透明度)globalCompositeOperation: string (混合模式)zIndex: number (默认: 0) - 值越大渲染层级越高draggable: boolean (默认: false)cursor: string (默认: 'default') - 悬停时的 CSS 光标样式- 描边与填充:
fill,stroke,lineWidth,lineDash,lineDashOffset,lineCap,lineJoin - 滤镜与阴影:
filter,shadowColor,shadowBlur,shadowOffsetX,shadowOffsetY - 事件:
onClick,onDoubleClick,onMouseDown,onMouseUp,onMouseMove,onMouseEnter,onMouseLeave,onWheel,onDragStart,onDragMove,onDragEnd - 节点方法:
cache(),clearCache(),toJSON(),toDataURL(),to(config)
width: numberheight: numbercornerRadius: number | number[] (圆角)
radius: number
radiusX: numberradiusY: number
sides: number (边数)radius: number
numPoints: number (角数)innerRadius: numberouterRadius: number
innerRadius: numberouterRadius: numberangle: number (弧度)
text: stringfontSize: numberfontFamily: stringfontStyle,fontWeight,fontVariant: 字体样式width: number (最大宽度,用于自动换行)align: 'left' | 'center' | 'right'verticalAlign: 'top' | 'middle' | 'bottom'lineHeight: number
data: string (SVG 路径数据)fill: stringstroke: stringlineWidth: number
points: number[] (坐标数组[x1, y1, x2, y2, ...])stroke: string (颜色)lineWidth: numberlineCap: 'butt' | 'round' | 'square'lineJoin: 'bevel' | 'round' | 'miter'closed: boolean (是否闭合路径)
src: string (图片 URL)image: HTMLImageElement (预加载的图片对象)width: numberheight: numberfilters: FilterFunction[] (像素级滤镜,内置Filters.Grayscale,Filters.Invert,Filters.Sepia,Filters.Brightness)
clip: boolean (开启裁剪)clipX,clipY,clipWidth,clipHeight: 裁剪矩形定义
target: Node (要依附的节点)borderStroke: string (边框颜色)handleFill: string (手柄颜色)keepRatio: boolean (保持宽高比)
内置 AssetManager 负责高效加载、缓存和去重图片资源。<Image> 组件会自动利用此系统。
支持对任意节点应用实时视觉特效。
<Image
src="photo.png"
filter="blur(5px) grayscale(50%)"
/>
<Rect
fill="white"
shadowColor="black"
shadowBlur={10}
/>根据交互状态自动更新光标。
- 悬停在可拖拽对象上显示
grab。 - 拖拽时显示
grabbing。 - Transformer 手柄会显示正确的调整光标(支持旋转)。
- 支持通过
cursor属性自定义光标。
支持基于宽度的自动换行和对齐。
<Text
text="这是一段很长的文本,会自动换行显示。"
width={200}
align="center"
/>用于交互式缩放和旋转节点的 UI 组件。
<Transformer target={selectedNode} />全面支持移动设备的触摸交互(拖拽、点击)。
- 视锥体剔除 (Frustum Culling): 自动跳过视口外的对象渲染。
- 智能脏矩形: 优化的渲染循环。
src/
├── core/ # 引擎核心 (场景图, 节点, 容器, 矩阵, 事件系统)
├── shapes/ # 图形实现 (Rect, Circle, Path 等)
├── react/ # React 绑定 (Canvas, Shapes, Context)
└── types/ # 类型定义
examples/ # 演示应用
使用 Vitest 运行测试套件:
npm test