定制化
@ydesign/react-editor 从第一天起就是为"二次开发"而设计的:你可以把它当作一个完整的、开箱即用的设计编辑器直接接入业务,也可以逐层下钻,替换、删除、新增任意一块 UI 或画布能力,最终得到一个完全属于你自己产品的编辑器。
本文介绍开发中最常见的 8 类定制场景,以及对应的真实 API。
💡 所有下文提到的
store都是通过createStore({ key })或createDesignEditorApp(...)返回的 MobX-State-Tree 实例;所有提到的store.editor则是@ydesign/core的Editor实例,持有全部画布 Handler。
1. 侧边面板(SidePanel)的完全控制
左侧面板是业务差异最大的区域。@ydesign/react-editor 不把它写死,而是以 Section 数组 的形式暴露,所有内置 Section 都可以按名称选用、自由排序、或整体替换。
1.1 只显示需要的面板
给 createDesignEditorApp 或 <SidePanel /> 传一个名字数组即可:
import { createDesignEditorApp } from '@ydesign/react-editor';
createDesignEditorApp({
container: document.getElementById('root')!,
key: 'YOUR_API_KEY',
// 只显示这 5 个面板
sections: ['templates', 'text', 'photos', 'shapes', 'background'],
});
内置可选的 Section 名称:
| 名称 | 说明 |
|---|---|
templates | 模板中心 |
text | 文字(字体、样式、预设) |
photos | 图片素材库 |
shapes | 形状 |
upload | 上传本地素材 |
background | 背景色 / 背景图 |
layers | 图层管理 |
size | 画布尺寸 |
idphoto | 证件照 |
inpaint | AI 消除笔 |
1.2 移除某个默认面板
import {
SidePanel,
TemplatesSection,
TextSection,
PhotosSection,
ShapesSection,
LayersSection,
// 不再 import BackgroundSection —— 它就不会出现
} from '@ydesign/react-editor/side-panel';
const sections = [TemplatesSection, TextSection, PhotosSection, ShapesSection, LayersSection];
<SidePanel store={store} sections={sections} />;
1.3 增加一个自定义面板
一个 Section 只需要三件事:name、Tab(侧边竖向的图标按钮)、Panel(点击后右侧展开的内容)。
import { observer } from 'mobx-react-lite';
import { SectionTab } from '@ydesign/react-editor/side-panel';
import type { Section } from '@ydesign/react-editor/side-panel';
import { Sparkles } from 'lucide-react';
const AIPanelSection: Section = {
name: 'ai',
Tab: observer(props => (
<SectionTab name="AI 助手" {...props}>
<Sparkles size={20} />
</SectionTab>
)),
Panel: observer(({ store }) => {
return (
<div>
<h3>从 AI 生成图片</h3>
<button
onClick={async () => {
const url = await callYourAIService();
// 直接通过 store.editor 往画布添加元素
store.editor?.objectsHandler.addImage({ src: url });
}}
>
生成
</button>
</div>
);
}),
};
// 与内置 Section 组合
import { DEFAULT_SECTIONS } from '@ydesign/react-editor/side-panel';
<SidePanel store={store} sections={[...DEFAULT_SECTIONS, AIPanelSection]} />;
1.4 替换某个默认面板(比如接自家素材库)
替换 PhotosSection 的内容非常简单 —— 自己造一个 name: 'photos' 的 Section,放在 DEFAULT_SECTIONS 之前就会自动覆盖:
import { DEFAULT_SECTIONS } from '@ydesign/react-editor/side-panel';
const MyPhotosSection: Section = {
name: 'photos',
Tab: /* ... */,
Panel: observer(({ store }) => {
const [list, setList] = useState([]);
useEffect(() => {
fetch('/my-api/photos').then(r => r.json()).then(setList);
}, []);
return (
<div>
{list.map(item => (
<img
key={item.id}
src={item.thumbnail}
onClick={() => store.editor?.objectsHandler.addImage({ src: item.url })}
/>
))}
</div>
);
}),
};
const sections = DEFAULT_SECTIONS.map(s => (s.name === 'photos' ? MyPhotosSection : s));
2. 工具栏(Toolbar)定制
顶部工具栏会根据当前选中的元素类型(text / image / path / 多选等)自动切换。可以通过 components 插槽在默认工具栏右侧追加自己的按钮:
import Toolbar from '@ydesign/react-editor/toolbar';
import { DownloadButton } from '@ydesign/react-editor/toolbar/download-button';
<Toolbar
store={store}
components={{
ActionControls: () => (
<>
<MyCloudSaveButton store={store} />
<DownloadButton store={store} />
</>
),
}}
/>;
想替换整条工具栏?直接用自己的组件代替 <Toolbar /> 即可,所有内部按钮(DownloadButton、DuplicateButton、RemoveButton、LockButton、HistoryButtons 等)都是独立导出的,可以自由组合:
import { HistoryButtons } from '@ydesign/react-editor/toolbar/history-buttons';
import { DuplicateButton } from '@ydesign/react-editor/toolbar/duplicate-button';
import { RemoveButton } from '@ydesign/react-editor/toolbar/remove-button';
const MyToolbar = ({ store }) => (
<div className="my-toolbar">
<HistoryButtons store={store} />
<DuplicateButton store={store} />
<RemoveButton store={store} />
{/* ...自定义按钮 */}
</div>
);
3. 画布(Workspace)定制
<Workspace /> 本身作为画布容器,外观通过 CSS 调整,行为通过 store.editor 里的 Handler 调整。
3.1 调整画布外观
/* 你的全局 CSS */
.polotno-app-container {
background: #1e1e1e; /* 编辑器整体背景 */
}
.polotno-workspace-container {
background: radial-gradient(circle, #2a2a2a, #181818);
}
3.2 调整画布行为
通过 @ydesign/core 的 Handler 即可:
// 切换缩放策略
store.editor?.zoomHandler.zoomToFit();
store.editor?.zoomHandler.setZoom(1.5);
// 开关辅助线
store.editor?.guidelinesHandler.enable();
store.editor?.guidelinesHandler.disable();
// 开关标尺
store.editor?.workareaHandler.setRulesVisible(true);
// 出血位
store.editor?.workareaHandler.setBleed(3); // mm
store.editor?.workareaHandler.setBleedVisible(true);
完整的 Handler 列表见 概览 一节。
4. 多语言(i18n)
编辑器内部文案通过一个响应式的翻译字典管理。默认是英文,切换或增补只需要一次调用。
import { setTranslations, getTranslations } from '@ydesign/react-editor';
// 1) 完整覆盖 —— 切换到中文
setTranslations({
sidePanel: {
text: '文字',
templates: '模板',
photos: '图片',
shapes: '形状',
upload: '上传',
background: '背景',
layers: '图层',
size: '尺寸',
myFonts: '我的字体',
uploadTip: '将文件拖到此处,或点击上传按钮',
},
toolbar: {
undo: '撤销',
redo: '重做',
opacity: '透明度',
position: '位置',
layering: '图层',
alignLeft: '左对齐',
flipHorizontally: '水平翻转',
// ...
},
});
// 2) 想知道完整的 key 结构?
console.log(getTranslations());
自定义的组件里也可以用同一套系统:
import { observer } from 'mobx-react-lite';
import { translate, t } from '@ydesign/react-editor/utils/l10n';
const MyPanel = observer(() => {
return <div>{translate('sidePanel.myCustomLabel')}</div>;
// t 是 translate 的别名
});
5. 素材与资源:上传、API、后端对接
5.1 自定义图片上传
默认情况下本地上传会转成 base64,对生产环境 JSON 会越来越臃肿。强烈建议把图片上传到你自己的对象存储:
import { setUploadFunc } from '@ydesign/react-editor/side-panel/upload-panel';
setUploadFunc(async (file: File) => {
const formData = new FormData();
formData.append('file', file);
const res = await fetch('https://your-api.com/upload', {
method: 'POST',
body: formData,
});
const { url } = await res.json();
return url; // 返回可在 <img> 直接使用的短链
});
5.2 切换后端域名 / 替换接口实现
@ydesign/react-editor 内置了几条 API(字体列表、模板列表、图片上传、AI 消除笔等)。你可以一次性切换域名:
import { setBaseURL } from '@ydesign/react-editor';
setBaseURL('https://api.your-company.com');
或者逐条替换某个接口的具体实现:
import { setAPI } from '@ydesign/react-editor';
// 把模板列表接口指向自家系统
setAPI('templateList', () => ({
url: 'https://your-api.com/v2/design-templates',
}));
// 上传图片 —— 支持 method / url / data
setAPI('uploadImage', () => ({
method: 'POST',
url: 'https://your-api.com/assets/upload',
}));
当前可替换的 API key:fontList、templateList、templateDetail、uploadImage、psdParse、createRecord、updateRecord、recordDetail、resourcesDetail、teamFontList、inpaint。
5.3 自定义背景色预设
import { setBackgroundColorsPreset } from '@ydesign/react-editor/side-panel/background-panel';
setBackgroundColorsPreset(['#ffffff', '#000000', '#f5f5f5', '#ff4d4f', '#1677ff', '#52c41a']);
5.4 资源加载超时
用于大图片 / 自定义字体加载等慢资源场景:
import { setAssetLoadTimeout, setFontLoadTimeout, setFontLoadTimeoutCallback } from '@ydesign/react-editor';
setAssetLoadTimeout(30_000); // 30s
setFontLoadTimeout(15_000);
setFontLoadTimeoutCallback(msg => message.warning(msg));
6. 数据加载与导出
store 本身就是你的数据边界。加载、保存、导出、协同都通过它完成。
6.1 从后端加载设计
const json = await fetch('/api/designs/42').then(r => r.json());
store.loadJSON(json);
6.2 保存 / 自动同步
import { reaction } from 'mobx';
// 任意状态变化都自动 debounce 上报
let timer: any;
reaction(
() => store.toJSON(),
json => {
clearTimeout(timer);
timer = setTimeout(() => {
fetch('/api/designs/42', {
method: 'PUT',
body: JSON.stringify(json),
});
}, 1000);
}
);
6.3 导出图片 / PDF
所有导出能力都挂在 store 或 editor 上:
// 导出当前画布为 PNG
const dataUrl = await store.toDataURL({ multiplier: 2, format: 'png' });
// 导出为 Blob 并下载
const blob = await store.toBlob({ multiplier: 2 });
导出可接收 ExportOptions:multiplier / format ('jpeg' 'png' 'webp') / quality / width / height / filter 等。
7. 主题与样式
@ydesign/react-editor 使用 Ant Design v6 + styled-components + Tailwind v4 构建 UI。
7.1 Antd 主题(色板、圆角、字体)
把整个编辑器包在 <ConfigProvider /> 里:
import { ConfigProvider, theme } from 'antd';
import { DesignEditorApp } from '@ydesign/react-editor';
<ConfigProvider
theme={{
algorithm: theme.darkAlgorithm, // 暗色主题
token: {
colorPrimary: '#ff6a00',
borderRadius: 6,
fontFamily: 'Inter, -apple-system, sans-serif',
},
}}
>
<DesignEditorApp store={store} />
</ConfigProvider>;
7.2 覆盖局部样式
编辑器的容器类名是稳定的,直接在全局 CSS 里覆盖即可:
.polotno-app-container {
font-family: 'Inter', sans-serif;
}
.polotno-side-panel-tab {
color: #999;
}
.polotno-side-panel-tab.active {
color: #ff6a00;
}
8. 下钻到 Core:扩展画布能力
如果你要做的事情连 Handler 层都没提供(例如新增一类自定义元素,接入二维码 / 条形码 / 图表),可以直接扩展 @ydesign/core。
8.1 自定义快捷键
// 默认快捷键是内置的,增补自己的一套
store.editor?.hotkeyHandler.bind({
'ctrl+s, command+s': e => {
e.preventDefault();
saveToServer();
},
'ctrl+shift+d, command+shift+d': () => {
store.editor?.objectsHandler.duplicate();
},
});
8.2 监听画布事件
Editor 继承自 EventManager,可以订阅内部事件:
store.editor?.on('object:added', obj => {
console.log('新增元素', obj);
});
store.editor?.on('selection:changed', ids => {
console.log('选中变化', ids);
});
8.3 写一个自己的 Handler(进阶)
所有内置 Handler 都继承自 Base,共享 editor / state / config / canvas 四个上下文。照同样的模式写一个挂到 Editor 上,就能享受和内置 Handler 一致的生命周期管理:
import { Base } from '@ydesign/core';
export class QRCodeHandler extends Base {
add(text: string) {
// 调用自家 QR 生成逻辑,再通过 canvas 插入元素
const svg = generateQRCode(text);
this.editor.objectsHandler.addSVG({ src: svg });
}
}
延伸阅读
如果你的定制场景在本文没有覆盖,欢迎去 GitHub 提 Issue,或直接 PR 补充这份文档。