Fabric.js自定义序列化问题:避免图片src序列化及解决对象丢失
解决Fabric.js自定义图片类序列化时移除src并正确反序列化的问题
我来帮你搞定这个问题——你遇到的核心问题是自定义fabric.Image子类的序列化/反序列化逻辑写得不对,尤其是fromObject方法的实现不符合Fabric.js的预期,再加上初始化方式的小错误,导致反序列化后对象丢失。下面是完整的解决方案:
问题分析
- 你之前的
initialize方法参数不符合fabric.Image的规范:fabric.Image的构造函数第一个参数是图片元素(或canvas),第二个才是配置选项,你之前把options当成第一个参数传,导致图片实例没有正确初始化。 fromObject方法误用了enlivenObjects:这个方法是用来处理包含子对象的复合元素(比如Group、Canvas)的,而你的MyImage是单个图片对象,完全不需要这个逻辑。- 反序列化时没有根据自定义属性重新加载图片:你删除了
src,但没有提供从自定义标识(比如entetyReference)重新获取图片源的逻辑,Fabric.js不知道怎么重建图片。
修正后的完整代码
1. 正确定义自定义图片类
fabric.MyImage = fabric.util.createClass(fabric.Image, { // 必须指定type,Fabric.js反序列化时靠这个识别类 type: 'MyImage', /** * 初始化方法,遵循fabric.Image的参数规范 * @param {HTMLImageElement|HTMLCanvasElement} element 图片元素 * @param {Object} options 配置选项 */ initialize: function(element, options) { // 调用父类的初始化方法 this.callSuper('initialize', element, options); // 初始化自定义属性 this.set('entetyReference', options?.entetyReference || ''); this.set('artIndexes', options?.artIndexes || ''); }, /** * 自定义序列化逻辑,只保留需要的属性 * @param {string[]} propertiesToInclude 需要额外包含的属性 */ toObject: function(propertiesToInclude) { // 调用父类toObject,传入自定义属性列表,确保它们被序列化 const baseObj = this.callSuper('toObject', [ 'entetyReference', 'artIndexes', // 这里可以添加其他你需要序列化的自定义属性 ]); // 删除不需要的src属性,减小序列化体积 delete baseObj.src; return baseObj; } // 渲染逻辑直接继承父类即可,不需要重写,除非你有自定义渲染需求 // _render: function(ctx) { // this.callSuper('_render', ctx); // } });
2. 实现正确的fromObject反序列化方法
这个方法的核心是根据你自定义的entetyReference重新获取图片源,然后创建MyImage实例:
/** * 从序列化对象重建MyImage实例 * @param {Object} object 序列化后的对象 * @param {Function} callback 回调函数,返回重建后的实例 */ fabric.MyImage.fromObject = function(object, callback) { // 这里需要你自己实现:根据entetyReference获取对应的图片URL // 比如你可以维护一个映射表,或者通过接口获取 const imageSource = getImageSourceByReference(object.entetyReference); // 使用Fabric.js内置的fromURL加载图片,然后创建MyImage实例 fabric.Image.fromURL(imageSource, function(imgElement) { // 实例化MyImage,复制所有必要的属性(位置、缩放、旋转等) const myImage = new fabric.MyImage(imgElement, { entetyReference: object.entetyReference, artIndexes: object.artIndexes, // 复制Fabric.js内置的视觉属性,确保还原图片状态 left: object.left, top: object.top, scaleX: object.scaleX, scaleY: object.scaleY, angle: object.angle, opacity: object.opacity, flipX: object.flipX, flipY: object.flipY, selectable: object.selectable }); // 回调返回重建后的实例 callback(myImage); }); }; // 标记该类的反序列化为异步(因为需要加载图片) fabric.MyImage.async = true; // 示例:根据entetyReference获取图片源的方法(你需要根据自己的业务实现) function getImageSourceByReference(ref) { // 比如从映射表获取,或者拼接URL const imageMap = { 'ref1': '/images/entity1.png', 'ref2': '/images/entity2.png' }; return imageMap[ref] || '/images/default.png'; }
3. 正确添加图片到Canvas
// 先通过Fabric.js加载图片元素,再创建MyImage实例 const entetyRef = 'ref1'; // 你的实体标识 const imageSource = getImageSourceByReference(entetyRef); fabric.Image.fromURL(imageSource, function(imgElement) { const img = new fabric.MyImage(imgElement, { entetyReference: entetyRef, artIndexes: 'index-001' // 你的自定义索引 }); this.canvas.add(img); }.bind(this)); // 确保this指向正确的上下文
使用序列化和反序列化
// 序列化Canvas(此时生成的JSON不会包含src,只保留自定义属性和必要的视觉属性) const canvasJSON = this.canvas.toJSON(); // 反序列化Canvas this.canvas.loadFromJSON(canvasJSON, function() { // 反序列化完成后重新渲染 this.canvas.renderAll(); }.bind(this));
关键注意事项
- 确保
getImageSourceByReference方法能根据你的entetyReference正确返回图片URL,这是反序列化的核心。 - 如果有其他自定义属性,记得在
toObject的propertiesToInclude数组中添加,同时在fromObject中复制到新实例。 - 所有Fabric.js内置的视觉属性(left、top、scaleX等)都要在
fromObject中复制,否则反序列化后的图片状态会丢失。
内容的提问来源于stack exchange,提问作者Sohail Faruqui
相关产品推荐
相关产品推荐

