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

如何以归一化坐标存储Fabric.js对象以适配不同画布尺寸?

实现Fabric.js坐标归一化存储与加载

核心思路是导出时将对象的绝对坐标/尺寸转换为相对于画布宽高的0-1比例值,加载时再根据当前画布尺寸反转换为绝对坐标,具体步骤如下:

一、导出归一化JSON

  1. 获取当前画布的原始宽高
  2. 遍历所有对象,将坐标、尺寸等属性转换为归一化值
  3. 调用toJSON()导出处理后的JSON

示例代码:

// 获取原始画布尺寸
const originalCanvasWidth = canvas.width;
const originalCanvasHeight = canvas.height;

// 遍历所有对象,转换为归一化属性
canvas.getObjects().forEach(obj => {
  // 基础坐标与尺寸
  obj.set({
    left: obj.left / originalCanvasWidth,
    top: obj.top / originalCanvasHeight,
    width: obj.width / originalCanvasWidth,
    height: obj.height / originalCanvasHeight
  });

  // 根据对象类型处理特殊属性
  switch(obj.type) {
    case 'circle':
      obj.radius = obj.radius / originalCanvasWidth; // 按画布宽度归一化半径
      break;
    case 'line':
      obj.x1 = obj.x1 / originalCanvasWidth;
      obj.x2 = obj.x2 / originalCanvasWidth;
      obj.y1 = obj.y1 / originalCanvasHeight;
      obj.y2 = obj.y2 / originalCanvasHeight;
      break;
    case 'text':
      obj.fontSize = obj.fontSize / originalCanvasWidth; // 可选:让文本大小随画布缩放
      break;
    // 其他类型(如矩形、多边形等)按需扩展
  }
});

// 导出归一化JSON
const normalizedJSON = canvas.toJSON();
// 保存到文件(这里省略文件保存逻辑,可使用浏览器Blob或Node.js文件系统API)

二、加载并反归一化

  1. 获取当前画布的目标宽高
  2. 调用loadFromJSON()加载JSON
  3. 在加载完成的回调中,遍历所有对象,将归一化属性转换为当前画布的绝对坐标
  4. 更新对象坐标并重新渲染画布

示例代码:

// 获取当前画布尺寸
const currentCanvasWidth = canvas.width;
const currentCanvasHeight = canvas.height;

// 加载归一化JSON
canvas.loadFromJSON(normalizedJSON, () => {
  // 反归一化所有对象属性
  canvas.getObjects().forEach(obj => {
    // 基础坐标与尺寸
    obj.set({
      left: obj.left * currentCanvasWidth,
      top: obj.top * currentCanvasHeight,
      width: obj.width * currentCanvasWidth,
      height: obj.height * currentCanvasHeight
    });

    // 处理特殊属性
    switch(obj.type) {
      case 'circle':
        obj.radius = obj.radius * currentCanvasWidth;
        break;
      case 'line':
        obj.x1 = obj.x1 * currentCanvasWidth;
        obj.x2 = obj.x2 * currentCanvasWidth;
        obj.y1 = obj.y1 * currentCanvasHeight;
        obj.y2 = obj.y2 * currentCanvasHeight;
        break;
      case 'text':
        obj.fontSize = obj.fontSize * currentCanvasWidth;
        break;
    }

    obj.setCoords(); // 必须调用,更新对象的边界框与交互区域
  });

  canvas.renderAll(); // 重新渲染画布
});

注意事项

  • 如果不需要某些属性随画布缩放(比如固定大小的图标),可以跳过对应属性的归一化处理
  • 对于带有旋转、斜切等变换的对象,旋转角度(rotation)、斜切角度(skewX/skewY)无需归一化,因为这些是对象自身的相对变换
  • 若需要批量处理所有对象类型,可封装通用的归一化/反归一化函数,避免重复代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:18:36