Skip to content

从第一张画布开始 ​

SkyNote 是一组可组合的画布 SDK。先使用 Editor 和 Leafer 渲染器创建编辑器,再按需要扩展节点、插件和产品界面。

Beta 文档预览

文档面向 0.1.0-beta.1 目标版本。当前 npm 包尚未确认公开可安装,下方安装命令为发布后的接入方式;发布前请在源码工作区体验。页面中的基础画布直接运行当前源码,不代表 npm 产物已完成验收。

安装与容器 ​

发布后安装:

sh
npm install @skynote/editor @skynote/renderer-leafer

容器必须有确定的宽高。页面退出时调用 destroy(),释放输入监听、渲染器和插件资源。

html
<div id="canvas" style="height: 480px; position: relative;"></div>
<script type="module" src="./main.ts"></script>

一个完整示例 ​

创建 basic.ts。下面是完整文件,只依赖已安装的两个包,不依赖本站主题或首页场景;它也用于下方真实演示。

ts
import { SkyNoteEditor } from '@skynote/editor';
import { LeaferAdapter } from '@skynote/renderer-leafer';

/** 运行示例与页面源码共用此文件;所有文档实体通过公开语义命令创建。 */
export function mountExample(
  container: HTMLElement,
  populate?: (editor: SkyNoteEditor<HTMLElement>) => void
): SkyNoteEditor<HTMLElement> {
  const editor = new SkyNoteEditor<HTMLElement>({ renderer: new LeaferAdapter(), initialView: 'preserve' });
  try {
    editor.mount(container);
    editor.engine.documentStore.batch(() => {
      if (populate) {
        populate(editor);
        return;
      }
      const note = editor.createNode({
        nodeType: 'sticky',
        x: 0,
        y: 0,
        width: 190,
        height: 150,
        options: { text: '一个新想法\n双击,写下你的灵感。' }
      });
      const shape = editor.createNode({
        nodeType: 'shape',
        x: 300,
        y: 35,
        width: 180,
        height: 70,
        options: { label: '连接,让思路更清晰' }
      });
      if (!note || !shape) throw new Error('无法创建示例节点');
      editor.updateNodeStyle([shape], { fill: '$teal', radius: 16 });
      editor.engine.documentStore.createConnector({
        connectorType: 'curved',
        from: { nodeId: note },
        to: { nodeId: shape }
      });
      editor.createNode({
        nodeType: 'free-text',
        x: 70,
        y: 210,
        width: 390,
        options: { text: '拖动节点 · 双击编辑 · 空格拖动画布' }
      });
    }, '创建演示内容');
    editor.engine.documentStore.clearHistory();
    editor.fitContent();
    return editor;
  } catch (error) {
    editor.destroy();
    throw error;
  }
}

再创建同目录的 vanilla.ts:

ts
import { mountExample } from './basic';

/** 将basic.ts和本文件放在同一目录,在容器进入DOM后调用。 */
export function mountCanvasPage(container: HTMLElement): () => void {
  const editor = mountExample(container);
  return () => editor.destroy();
}

最后在同目录创建main.ts,HTML中的模块脚本会执行该入口:

ts
import { mountCanvasPage } from './vanilla';

const container = document.getElementById('canvas');
if (!container) throw new Error('请先在页面中提供id为canvas的容器');

/** 独立页面加载即挂载;SPA路由离开时调用此清理函数。 */
export const destroyCanvasPage = mountCanvasPage(container);

在自己的SPA路由退出时调用导出的destroyCanvasPage()。纯TypeScript接入需要项目自己的构建工具,以上HTML与三个TS文件可放入现有的Vite等工程中;它们不是直接双击HTML就能运行的无构建脚本。

基础画布交互体验

免费体验,点击后加载;修改只保留在当前页面。

在 Vue 中使用 ​

在 onMounted 中挂载,在 onBeforeUnmount 中销毁。编辑器实例保存在普通变量,或使用 markRaw / shallowRef;不要放入深度响应式代理。

完整单文件组件、SSR与异步卸载处理见Vue接入。

渲染器依赖浏览器环境。SSR / 静态生成站点应在客户端动态导入,不能在服务端初始化画布。

从源码工作区运行 ​

维护者已检出四个基础包与本站时,在工作区根目录执行:

sh
pnpm install
pnpm --filter @skynote/docs-site dev

本站使用端口 5174。高级插件演示的宿主配置见示例与体验。

下一步阅读基础概念和常用操作。

常见问题 ​

现象检查方法
画布空白确认容器已经挂载,宽高不为零;在隐藏面板中初始化后需等待布局就绪
window / document 未定义把运行时导入与挂载放到客户端生命周期,不在SSR阶段创建Leafer实例
快捷键没有响应先点击画布,让输入作用域获得焦点;宿主工具栏按下时应保留画布焦点
重复挂载后监听越来越多页面卸载时调用destroy();暂时卸载渲染器才使用unmount()
安装命令找不到包当前仍是Beta目标文档,先按上方源码工作区方式运行;不将目标版本视为已发包

可组合的画布能力,从一个想法开始。