Fabric JS v6升级后自定义Qrcode类无法被loadFromJSON识别
问题描述
将Fabric JS版本升级至v6+后,因该版本移除了createClass方法,改写代码实现了继承fabric.Group的自定义QrcodeObject类。组件可正常渲染,使用canvas.toJSON()保存数据也正常,但调用loadFromJSON()恢复画布数据时,出现错误提示Fabric: No class registered for Qrcode。
自定义类代码
import * as fabric from "fabric"; import { QRCodeSVG } from "qrcode.react"; import { FabricObjectMetadataOptions } from "../common/interfaces"; import { convertJSXToXMLString } from "../utils"; export class QrCodeObject extends fabric.Group { static type = "Qrcode"; private element: fabric.FabricObject; constructor(options: QrCodeObjectProps) { super([], { ...options, lockRotation: true, lockSkewingX: true, lockSkewingY: true, }); this.element = {} as fabric.FabricObject; this.initialize(options); return this; } private async initialize(options: QrCodeObjectProps) { const svgString = QrCodeObject.toSVGString(options); this.element = await this.createSVGFromString(svgString, options); this.add(this.element); this.lockMiddleControls(); } private createSVGFromString( svgString: string, options: QrCodeObjectProps ): Promise<fabric.FabricObject> { return new Promise((resolve) => { fabric.loadSVGFromString(svgString).then((resultSVG) => { const obj = fabric.util.groupSVGElements( resultSVG.objects as fabric.FabricObject[], resultSVG.options ); obj.set({ metadata: options.metadata, }); resolve(obj); }); }); } static toSVGString({ content, widthContainer, level = "L", bgColor = "#ffffff", fgColor = "#000000", }: QrCodeReactProps) { const svg = QRCodeSVG({ value: content, size: widthContainer, level, bgColor, fgColor, }); const xmlString = convertJSXToXMLString(svg); return xmlString; } private lockMiddleControls() { this.setControlsVisibility({ mt: false, mb: false, ml: false, mr: false, bl: true, br: true, tl: true, tr: true, mtr: false, }); } toJSON() { return super.toObject(); } } export interface QrCodeReactProps { content: string; widthContainer: number; bgColor?: string; fgColor?: string; level?: "L" | "M" | "Q" | "H"; includeMargin?: boolean; imageSettings?: { src: string; height: number; width: number; x?: number; y?: number; excavate?: boolean; }; } export interface QrCodeObjectProps extends QrCodeReactProps, Partial<fabric.FabricObjectProps> { id: string; name: string; metadata: FabricObjectMetadataOptions; content: string; widthContainer: number; } declare module "fabric" { namespace fabric { class Qrcode extends QrCodeObject { constructor(element: any, options: any); } } }
保存与恢复逻辑代码
const handlers = { save: () => { saveData(canvas?.toJSON()); }, restore: (jsonData: string) => canvas?.loadFromJSON(jsonData, () => { canvas?.renderAll(); }); };
解决方案
Fabric JS v6+要求自定义类必须手动注册到全局类注册表,且需实现fromObject静态方法,才能让loadFromJSON正确识别并重建对象。以下是具体修复步骤:
1. 实现fromObject静态方法
在QrCodeObject类中添加该方法,负责从JSON数据重建对象,处理异步初始化逻辑。
2. 注册自定义类
将QrCodeObject注册到Fabric全局注册表,让Fabric能识别"Qrcode"类型。
3. 完善toJSON方法
确保JSON序列化时包含二维码的核心配置属性,否则恢复时无法重建正确的二维码。
修改后的自定义类代码
import * as fabric from "fabric"; import { QRCodeSVG } from "qrcode.react"; import { FabricObjectMetadataOptions } from "../common/interfaces"; import { convertJSXToXMLString } from "../utils"; export class QrCodeObject extends fabric.Group { static type = "Qrcode"; private element: fabric.FabricObject; // 存储二维码核心配置,用于JSON序列化 private qrConfig: QrCodeReactProps; constructor(options: QrCodeObjectProps) { super([], { ...options, lockRotation: true, lockSkewingX: true, lockSkewingY: true, }); this.element = {} as fabric.FabricObject; // 保存二维码配置 this.qrConfig = { content: options.content, widthContainer: options.widthContainer, bgColor: options.bgColor, fgColor: options.fgColor, level: options.level, includeMargin: options.includeMargin, imageSettings: options.imageSettings }; this.initialize(options); return this; } private async initialize(options: QrCodeObjectProps) { const svgString = QrCodeObject.toSVGString(this.qrConfig); this.element = await this.createSVGFromString(svgString, options); this.add(this.element); this.lockMiddleControls(); } private createSVGFromString( svgString: string, options: QrCodeObjectProps ): Promise<fabric.FabricObject> { return new Promise((resolve) => { fabric.loadSVGFromString(svgString).then((resultSVG) => { const obj = fabric.util.groupSVGElements( resultSVG.objects as fabric.FabricObject[], resultSVG.options ); obj.set({ metadata: options.metadata, }); resolve(obj); }); }); } static toSVGString({ content, widthContainer, level = "L", bgColor = "#ffffff", fgColor = "#000000", }: QrCodeReactProps) { const svg = QRCodeSVG({ value: content, size: widthContainer, level, bgColor, fgColor, }); const xmlString = convertJSXToXMLString(svg); return xmlString; } private lockMiddleControls() { this.setControlsVisibility({ mt: false, mb: false, ml: false, mr: false, bl: true, br: true, tl: true, tr: true, mtr: false, }); } // 重写toJSON,包含自定义属性 toJSON(propertiesToInclude: string[] = []) { return super.toObject([ ...propertiesToInclude, "qrConfig", "metadata", "id", "name" ]); } // 实现fromObject静态方法,支持异步重建 static async fromObject(object: any, callback?: (obj: QrCodeObject) => void) { const qrObj = new QrCodeObject({ ...object, ...object.qrConfig }); // 等待初始化完成 await qrObj.initialize(qrObj); callback?.(qrObj); return qrObj; } } // 注册自定义类到Fabric全局注册表 fabric.Qrcode = QrCodeObject; fabric.registerClass(QrCodeObject); export interface QrCodeReactProps { content: string; widthContainer: number; bgColor?: string; fgColor?: string; level?: "L" | "M" | "Q" | "H"; includeMargin?: boolean; imageSettings?: { src: string; height: number; width: number; x?: number; y?: number; excavate?: boolean; }; } export interface QrCodeObjectProps extends QrCodeReactProps, Partial<fabric.FabricObjectProps> { id: string; name: string; metadata: FabricObjectMetadataOptions; content: string; widthContainer: number; } declare module "fabric" { namespace fabric { class Qrcode extends QrCodeObject { constructor(element: any, options: any); } } }
修改恢复逻辑
由于fromObject是异步方法,调整恢复逻辑确保等待所有对象重建完成后再渲染:
const handlers = { save: () => { saveData(canvas?.toJSON()); }, restore: async (jsonData: string) => { if (!canvas) return; await canvas.loadFromJSON(jsonData); canvas.renderAll(); } };
关键说明
- 类注册:通过
fabric.registerClass(QrCodeObject)和fabric.Qrcode = QrCodeObject完成注册,Fabric解析JSON时会根据type: "Qrcode"匹配到该类。 fromObject方法:Fabric核心方法,负责将JSON数据转换为实例对象,必须处理异步初始化逻辑。toJSON方法:自定义属性需手动加入序列化列表,否则恢复时会丢失关键配置。
内容的提问来源于stack exchange,提问作者M_thew
相关产品推荐
相关产品推荐

