跳到主要内容

元素操作

Ydesign 的"元素"就是 Fabric.js 的 FabricObject。我们没有像 Polotno 那样封装一层 Element 类,所以所有能力既可以从 store 层调,也可以直接用 Fabric 的原生 API。

本页按功能分组列出常用操作。所有 store.editor.xxxHandler.xxx() 都是真实方法,从 @ydesign/coreObjectsHandler / LayerHandler / AlignmentHandler 等 Handler 暴露。


元素类型

常见的 type 取值(与 Fabric 保持一致):

type说明
textbox富文本框(多行、可调宽度的文本)
image位图图片
pathSVG 路径(来自形状面板、导入的 SVG)
rect / circle / ellipse / triangle基础几何形状
group组合,包含 objects: FabricObject[]
workarea工作区(背景板),自动创建、不可选中

⚠️ workarea 是一个特殊的 rect,ID 固定为 'workarea',由 WorkareaHandler 管理。它不是"用户元素",不会出现在选中结果中。


添加元素

添加已知结构的 JSON

// 文本
store.addElement({
type: 'textbox',
text: 'Hello Ydesign',
left: 100,
top: 100,
width: 400,
fontSize: 48,
fontFamily: 'Inter',
fill: '#111',
});

// 图片
store.addElement({
type: 'image',
src: 'https://cdn.example.com/photo.jpg',
left: 50,
top: 50,
scaleX: 0.5,
scaleY: 0.5,
});

// 形状(基础几何)
store.addElement({
type: 'rect',
left: 200,
top: 200,
width: 300,
height: 200,
fill: '#2253eb',
rx: 12, // 圆角
ry: 12,
});

addElement(options, isHistory?, autoSelect?)

  • options —— 元素 JSON
  • isHistory —— 是否计入撤销栈,默认 true
  • autoSelect —— 添加后是否自动选中,默认 true

路径(自定义形状)

// 一个五角星路径
store.editor?.objectsHandler.createPathElement(
'M 100,10 L 125,85 L 200,85 L 140,130 L 165,205 L 100,160 L 35,205 L 60,130 L 0,85 L 75,85 Z',
100, // left
100, // top
);

预设形状(形状面板的快捷入口)

store.editor?.objectsHandler.createShapeElement('roundRect', 50, 50);
store.editor?.objectsHandler.createShapeElement('triangle', 200, 200);
// ...详见 @ydesign/react-editor 里 `config.ts` 的 ShapePathFormulasKeys

修改元素属性

所有 Fabric FabricObject 的属性都可以修改:

// 作用于当前选中的全部元素
store.set({ opacity: 0.8 });
store.set({ angle: 45 });
store.set({ fill: '#ff6a00' });

// 作用于指定元素
store.set({ stroke: '#000', strokeWidth: 2 }, element);

// 也可以通过 handler 用 id 定位
store.editor?.objectsHandler.update({ left: 200, top: 300 }, 'element-id');

常用属性速查

类别属性
位置left, top
尺寸width, height, scaleX, scaleY
旋转angle
翻转flipX, flipY
透明opacity
填充fill(支持纯色 / 渐变对象)
描边stroke, strokeWidth, strokeDashArray
圆角(rect)rx, ry
文本text, fontSize, fontFamily, fontWeight, fontStyle, lineHeight, letterSpacing, textAlign
图片src, cropX, cropY, filters

文本专属

// 文本样式(作用在选区 / 整段)
store.editor?.objectsHandler.setTextStyle({
fontWeight: 'bold',
fontStyle: 'italic',
underline: true,
}, 'textbox-id');

阴影

store.editor?.objectsHandler.setShadow({
color: 'rgba(0,0,0,0.3)',
blur: 10,
offsetX: 4,
offsetY: 4,
});

查找元素

// 按 ID
store.getElementById('abc123');
store.editor?.objectsHandler.findOneById('abc123');

// 按条件回调(递归 group)
const firstText = store.find(e => e.type === 'textbox');

// 按 name(自定义业务字段)
store.editor?.objectsHandler.findByName('watermark');

// 按 name 删除
store.editor?.objectsHandler.removeByName('watermark');

// 拿到所有对象(不含 workarea)
const all = store.editor?.layerHandler.getEffectiveLayers();

选中 / 取消选中

// 选中单个
store.editor?.objectsHandler.select('element-id');

// 批量选中多个(生成 ActiveSelection)
store.editor?.objectsHandler.selectElements([el1, el2, el3]);

// 或通过 store 设置 ID 列表
store.selectElements(['id-1', 'id-2']);

// 取消选中
store.editor?.objectsHandler.deselect();

// 拿到当前选中
store.selectedElements; // FabricObject[](可能包含 group 本身)
store.selectedShapes; // FabricObject[](递归展开 group,不含 group)

删除 / 复制

// 删除当前选中
store.editor?.objectsHandler.remove();

// 按 ID 删除
store.editor?.objectsHandler.removeById('element-id');

// 批量删除
store.deleteElements(['id-1', 'id-2']);

// 复制当前选中
store.clone();

// 键盘移动(方向键微调位置)
store.editor?.objectsHandler.arrowMoving({
direction: 'up',
amount: 10,
id: 'element-id', // 不传则作用于当前选中
});

图层操作

Ydesign 的图层概念是:画布对象在 Fabric 内部数组中的顺序。越靠后的越在视觉上方。

// 向上 / 向下一层
store.moveElementsUp(['id-1']);
store.moveElementsDown(['id-1']);

// 置顶 / 置底
store.moveElementsTop(['id-1']);
store.moveElementsBottom(['id-1']);

// 判断能否移动(常用于按钮态)
store.canMoveElementsUp(['id-1']);
store.canMoveElementsTop(['id-1']);
store.canMoveElementsDown(['id-1']);
store.canMoveElementsBottom(['id-1']);

// 直接通过 handler 调
store.editor?.layerHandler.bringForward('id-1');
store.editor?.layerHandler.bringToFront('id-1');
store.editor?.layerHandler.sendBackwards('id-1');
store.editor?.layerHandler.sendToBack('id-1');

// 拿到图层列表(过滤掉 workarea 的"有效图层")
const layers = store.editor?.layerHandler.getEffectiveLayers();

组合 / 拆分

// 把当前选中的多个元素打包成 group
store.editor?.objectsHandler.group();

// 把选中的 group 拆散
store.editor?.objectsHandler.ungroup();

组也是 Fabric 的 Group 对象,group.getObjects() 可以拿到子元素数组。


对齐 / 分布

AlignmentHandler 负责,对选中元素生效:

const align = store.editor?.alignmentHandler;

align?.alignLeft();
align?.alignCenter(); // 水平居中
align?.alignRight();
align?.alignTop();
align?.alignMiddle(); // 垂直居中
align?.alignBottom();

// 等距分布
align?.distributeHorizontal();
align?.distributeVertical();

锁定

// 锁定选中元素(不可移动 / 缩放 / 旋转)
store.editor?.lockHandler.lock();

// 解锁
store.editor?.lockHandler.unlock();

// 判断是否锁定
store.editor?.lockHandler.isLocked(element);

图片专属:裁剪 / 滤镜 / 描边 / 消除笔

图片类元素有一系列专属能力:

// 裁剪(双击图片进入裁剪态,也可以手动调)
store.editor?.imageCropHandler.cropImg.onEnterCrop(imageElement);

// 滤镜(灰度、棕褐色、模糊、色相旋转等)
store.editor?.imageFiltersHandler.apply(imageElement, 'grayscale');
store.editor?.imageFiltersHandler.apply(imageElement, 'brightness', { value: 0.2 });

// 图片描边(支持虚线 / 内外描边)
store.editor?.imageStrokeHandler.setStroke(imageElement, {
color: '#000',
width: 4,
style: 'dashed',
position: 'outer',
});

// AI 消除笔(弹出蒙版,三种模式:brush / rect / lasso)
store.editor?.inpaintHandler.activate('brush');
store.editor?.inpaintHandler.setBrushSize(20);

// 导出蒙版 + 底图,交给后端做 inpainting
const { maskBlob, imageBlob } = await store.editor!.inpaintHandler.exportMaskAndImage();

详见 定制化


监听变化

监听画布对象变动(新增 / 删除 / 移动)

const dispose = store.on('change', objects => {
console.log('画布元素变动:共', objects.length, '个');
});
dispose();

监听核心层事件

Editor 本身就是事件总线(EventManager),可以订阅更细粒度的事件:

store.editor?.on('object:modified', obj => {
console.log('元素被修改', obj);
});

store.editor?.on('textbox:modified', obj => {
console.log('文字被修改', obj);
});

store.editor?.on('workarea:changed', ({ target }) => {
console.log('工作区尺寸变了', target.width, target.height);
});

store.editor?.on('history:changed', data => {
console.log('历史栈变化', data);
});

事件名来自 core 里的 Handler 发射,完整列表可以在 @ydesign/core 源码中按 this.editor.emit( 搜索。


与 Fabric.js 的关系

Ydesign 不会屏蔽 Fabric 的底层能力 —— 如果某个操作 Handler 没提供,你可以直接拿到 Fabric Canvas 自己调:

const fabricCanvas = store.editor!.customCanvas.canvas;

// 比如 Fabric 原生的 `forEachObject`
fabricCanvas.forEachObject(obj => {
console.log(obj.type, obj.id);
});

// 或者直接调 Fabric 的 setActiveObject
fabricCanvas.setActiveObject(someObj);
fabricCanvas.requestRenderAll();

强烈建议优先用 Handler 提供的方法,因为 Handler 会额外:

  • 维护响应式状态(自动同步到 store
  • 触发历史记录
  • 处理引导线 / 吸附 / 选中态
  • 保留撤销 / 重做的语义

直接 mutate Fabric 对象容易丢这些上下文。


下一步