React
@infinite-canvas-tutorial/react 提供 React 18/19 的便利封装。第一版复用 Spectrum Web Component,负责画布生命周期、类型化回调和 API 获取。
npm install @infinite-canvas-tutorial/react'use client';
import { useRef } from 'react';
import {
InfiniteCanvas,
type InfiniteCanvasHandle,
} from '@infinite-canvas-tutorial/react/spectrum';
export function Editor() {
const canvas = useRef<InfiniteCanvasHandle>(null);
return (
<>
<button onClick={() => canvas.current?.api?.undo()}>撤销</button>
<InfiniteCanvas
ref={canvas}
style={{ width: '100%', height: 600 }}
initialNodes={[
{ id: 'rect', type: 'rect', x: 50, y: 50, width: 100, height: 80, fill: '#ff8400' },
]}
onNodesChange={(nodes) => console.log(nodes)}
fallback={<span>正在加载画布…</span>}
/>
</>
);
}请为容器设置明确高度。浏览器依赖和自定义元素在客户端挂载时才加载,适用于 Next.js Client Component。默认入口和 /spectrum 目前导出同一个组件。
初始化与 API
initialNodes在每次创建画布时复制一次,在异步onReady完成后写入 ECS。 后续改变该属性不会覆盖用户编辑;更新图形请使用api.updateNodes()。initialAppState是初始化状态;后续通过api.setAppState()修改。ref.current.api和ref.current.element是实时 getter,初始化前为null。 后者指向真正的 Web Component,可监听额外 DOM 事件。onAPIChange(api)发布可用 API,卸载时传入null。theme同步 Spectrum UI 与画布主题。locale使用 Lit 的全局本地化状态, 同一页面上的画布共享语言。- 改变
renderer、shaderCompilerPath或initializationTimeout会重建画布。
事件与加载状态
可以传入 onChange、onNodesChange、onAppStateChange、 onSelectedNodesChange、onCameraZoomChange 和 onResize。 这些回调具有 TypeScript 类型,更新回调不会重建画布。
fallback 用于加载提示,renderError 自定义错误显示。onError 报告运行时加载、 就绪超时、语言设置和异步准备错误。initializationTimeout 默认为 30000 毫秒, 设为 0 可关闭运行时与 GPU 就绪超时检查。
插件与多画布
在组件外创建一个共享运行时,然后传给所有同时挂载的画布:
import { InfiniteCanvas, createCanvasRuntime } from '@infinite-canvas-tutorial/react';
import { LassoPlugin } from '@infinite-canvas-tutorial/lasso';
const runtime = createCanvasRuntime({
plugins: [LassoPlugin],
loadUI: () => import('@infinite-canvas-tutorial/lasso/spectrum'),
});
export function Compare() {
return (
<>
<InfiniteCanvas runtime={runtime} style={{ height: 400 }} />
<InfiniteCanvas runtime={runtime} style={{ height: 400 }} />
</>
);
}运行时自动包含 DefaultPlugins 和 UIPlugin。插件配置在启动时确定;修改插件前 需要卸载所有使用该运行时的画布。当前 Web Components 使用全局初始化队列, 因此同时使用不同运行时会报错。
每个画布拥有独立的 API 和订阅,最后一个画布卸载后才退出 App。 StrictMode 的立即挂载、清理、再次挂载会复用正在启动的 App。
React children 会放入 Web Component 的 light DOM,可使用已有的具名 slot。
异步准备
onReady(api, { signal }) 可以返回 Promise。在每次 await 后检查取消信号, 避免卸载后继续操作 API:
<InfiniteCanvas
initialNodes={nodes}
onReady={async (api, { signal }) => {
const response = await fetch('/assets/config.json', { signal });
const config = await response.json();
if (signal.aborted) return;
api.setAppState(config);
}}
/>上传、持久化和协作由宿主应用实现。Provider 和 selector hooks 将作为后续扩展。