如何以归一化坐标存储Fabric.js对象以适配不同画布尺寸?
实现Fabric.js坐标归一化存储与加载
核心思路是导出时将对象的绝对坐标/尺寸转换为相对于画布宽高的0-1比例值,加载时再根据当前画布尺寸反转换为绝对坐标,具体步骤如下:
一、导出归一化JSON
- 获取当前画布的原始宽高
- 遍历所有对象,将坐标、尺寸等属性转换为归一化值
- 调用
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)
二、加载并反归一化
- 获取当前画布的目标宽高
- 调用
loadFromJSON()加载JSON - 在加载完成的回调中,遍历所有对象,将归一化属性转换为当前画布的绝对坐标
- 更新对象坐标并重新渲染画布
示例代码:
// 获取当前画布尺寸 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
相关产品推荐
相关产品推荐

