You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Fabric.js中恢复多选缩放对象的原始尺寸?

Fabric.js 撤销功能缩放恢复异常问题

我正在基于Fabric.js实现撤销功能,当前代码在撤销位置修改时正常,但对象缩放后,仅能调整选框大小,无法恢复内部对象的尺寸。

现有撤销核心代码:

let groupObjects = lastAction.before._objects;
if (groupObjects) {
  let selectionGroup = new fabric.ActiveSelection(groupObjects, { canvas: canvas });
  selectionGroup.set({ ...lastAction.before });
  selectionGroup.setCoords();
  canvas.setActiveObject(selectionGroup);
}
canvas.renderAll();

lastAction.before的赋值逻辑:

lastAction = { before: fabric.util.object.clone(canvas.getActiveObjects()) }

我尝试遍历单个对象恢复属性,但问题依旧:

groupObjects.forEach((obj, index) => {
  let objectToRevert = canvas.getObjects().find(o => o.objectId === obj.objectId);
  objectToRevert.set({
    left: obj.left,
    top: obj.top,
    scaleX: obj.scaleX,
    scaleY: obj.scaleY,
    angle: obj.angle,
    width: obj.width,
    height: obj.height,
    flipX: obj.flipX,
    flipY: obj.flipY,
    skewX: obj.skewX,
    skewY: obj.skewY
  });
  objectToRevert.setCoords();
});

问题示意图


问题根源

  1. canvas.getActiveObjects()返回的是当前选中对象的引用集合,直接用fabric.util.object.clone克隆时,无法完整复制ActiveSelection内部对象的变换状态,属于浅拷贝层面的问题。
  2. 直接给ActiveSelection设置属性,只会修改选框的外观参数,不会同步更新内部对象的实际变换值。
  3. 遍历恢复时,可能因objectId匹配偏差、未触发对象重绘逻辑,导致属性设置未生效。

修复方案

1. 正确保存对象的完整状态

记录操作时,需要深度克隆每个选中对象的可序列化完整状态,而非直接克隆getActiveObjects()的结果:

// 修改操作记录的赋值逻辑
const activeObjects = canvas.getActiveObjects();
const clonedObjects = activeObjects.map(obj => {
  // 导出并克隆对象的核心状态属性
  return fabric.util.object.clone(obj.toObject([
    'objectId', 'left', 'top', 'scaleX', 'scaleY', 
    'angle', 'width', 'height', 'flipX', 'flipY', 
    'skewX', 'skewY'
  ]));
});
lastAction = { before: clonedObjects };

2. 撤销时逐个恢复对象状态

撤销时先逐个恢复对象的原始状态,再重新创建选中组保证选框与对象状态匹配:

// 修改撤销逻辑
lastAction.before.forEach(clonedObj => {
  const targetObj = canvas.getObjects().find(o => o.objectId === clonedObj.objectId);
  if (targetObj) {
    // 用克隆的完整状态覆盖当前对象
    targetObj.set(clonedObj);
    targetObj.setCoords();
  }
});

// 重新创建选中组,确保选框与对象状态同步
const targetObjects = lastAction.before
  .map(obj => canvas.getObjects().find(o => o.objectId === obj.objectId))
  .filter(Boolean);
const selectionGroup = new fabric.ActiveSelection(targetObjects, { canvas: canvas });
canvas.setActiveObject(selectionGroup);
canvas.renderAll();

3. 关键注意点

  • 确保每个画布对象都有唯一的objectId,避免对象匹配错误。
  • 使用toObject()导出可序列化状态再克隆,比直接克隆对象实例更可靠,能完整保留变换参数。
  • 恢复对象后必须调用setCoords()更新碰撞检测区域,最后调用renderAll()触发画布重绘。

内容的提问来源于stack exchange,提问作者threeFatCat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 01:22:33