Fabric JS 5.3.0自定义类无法序列化,触发TypeError报错
解决Fabric JS 5.3.0自定义类序列化问题
报错原因
你尝试设置fabric.labeledRect.fromObject时触发报错,核心原因有两点:
- 自定义的
LabeledRect类仅作为局部变量存在,未注册到Fabric全局命名空间,导致fabric.labeledRect不存在; - Fabric内置类均采用大驼峰命名(如
fabric.Rect),你误用了小写的labeledRect,不符合框架命名规范。
修正步骤
1. 注册自定义类到Fabric全局命名空间
在定义完LabeledRect类后,将其挂载到fabric对象,确保框架能识别该类:
fabric.LabeledRect = LabeledRect;
2. 正确实现fromObject静态方法
Fabric 5.x中,需将fromObject添加到已注册的类上,同时修正_fromObject的调用参数(传入正确的类引用):
fabric.LabeledRect.fromObject = function(object, callback) { return fabric.Object._fromObject(fabric.LabeledRect, object, callback); };
也可以在类定义时直接通过静态属性添加,更简洁:
var LabeledRect = fabric.util.createClass(fabric.Rect, { // 原有类属性和方法 }, { // 静态方法 fromObject: function(object, callback) { return fabric.Object._fromObject(LabeledRect, object, callback); } });
3. 补充初始化默认属性
你的_render方法使用了this.labelFont和this.labelFill,但初始化时未设置默认值会导致渲染错误,需在initialize中补充:
initialize: function(options) { options || (options = {}); this.callSuper('initialize', options); this.set({ width: 100, height: 50 }); // 添加字体、颜色默认值 this.set('labelFont', options.labelFont || '14px Arial'); this.set('labelFill', options.labelFill || '#000'); this.set('label', options.label || ''); }
4. 调整序列化/反序列化顺序
确保fromObject方法在序列化(JSON.stringify(canvas))之前定义,否则Fabric无法识别自定义类的序列化格式。
完整修正代码
var canvas = new fabric.Canvas("myCanvas"); canvas.setWidth(window.innerWidth); canvas.setHeight(window.innerHeight); window.addEventListener("resize", function () { canvas.setWidth(window.innerWidth); canvas.setHeight(window.innerHeight); }); // 定义自定义类 var LabeledRect = fabric.util.createClass(fabric.Rect, { type: 'labeledRect', initialize: function(options) { options || (options = {}); this.callSuper('initialize', options); this.set({ width: 100, height: 50 }); // 设置默认属性 this.set('labelFont', options.labelFont || '14px Arial'); this.set('labelFill', options.labelFill || '#000'); this.set('label', options.label || ''); }, toObject: function () { return fabric.util.object.extend(this.callSuper('toObject'), { label: this.get('label'), labelFont: this.get('labelFont'), labelFill: this.get('labelFill') }); }, _render: function(ctx) { // 先调用父类方法绘制基础矩形 this.callSuper('_render', ctx); ctx.font = this.labelFont; ctx.fillStyle = this.labelFill; ctx.fillText(this.label, -this.width/2, -this.height/2 + 20); } }, { // 静态fromObject方法 fromObject: function(object, callback) { return fabric.Object._fromObject(LabeledRect, object, callback); } }); // 注册到Fabric全局命名空间 fabric.LabeledRect = LabeledRect; // 创建实例并添加到画布 var labeledRect = new LabeledRect({ width: 100, height: 50, left: 100, top: 100, label: 'test', fill: '#faa' }); canvas.add(labeledRect); // 序列化与反序列化 var savedJson = JSON.stringify(canvas.toJSON()); console.log(savedJson); canvas.loadFromJSON(savedJson, function() { canvas.renderAll(); });
额外说明
- 使用
canvas.toJSON()代替直接JSON.stringify(canvas),这是Fabric推荐的序列化方式,能更准确导出画布数据; - 在
loadFromJSON的回调中执行renderAll(),确保反序列化完成后再重新渲染画布; _render方法中先调用this.callSuper('_render', ctx)绘制基础矩形,再绘制文本,避免覆盖父类的渲染逻辑。
内容的提问来源于stack exchange,提问作者Gaviota
相关产品推荐
相关产品推荐

