Issue Encountered with FabricJS: Unable to Execute 'drawImage' with Image Subclass

I'm working on a project that requires me to add images of different types to a canvas, save them as JSON, and then load them again. The unique property needed for each type is simply the differentiation in type.

To achieve this, I have created a new class based on fabric.Image called fabric.Icon:

fabric.Icon = fabric.util.createClass(fabric.Image, {
      type: "icon",
      initialize: function (element, options) {
        this.callSuper("initialize", element, options);
      },
      _render: function (ctx) {
        this.callSuper("_render", ctx);
      },
    });
    fabric.Icon.fromObject = function (object, callback) {
      return fabric.Object._fromObject("Icon", object, callback);
    };

After creating the fabric.Icon class, I use an SVG image to add a new object of this specific type to the canvas:

let newImage = new Image();    //HTML Image type, not FabricJS
newImage.onload = () => {
   let icon = new fabric.Icon(newImage);   
   this.canvas.add(icon);
};
newImage.src = "image.svg";

However, when I try to export the canvas into JSON using

let savedJSON = canvas.toJSON()

and then load it back with

canvas.loadFromJSON(savedJSON, function () {
    canvas.requestRenderAll();
}

I encounter the following error:

Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'

Everything works correctly when I use the standard fabric.Image class. Should I override another method in my subclass? Or is there a better way to differentiate between various Image types?

Answer №1

Although it's not a direct solution to the issue, I have discovered a method for effectively distinguishing between objects that persist during saving and loading. This involves setting a custom property on the Object.prototype by overriding its toObject method as illustrated in this example: Extending Fabric js toObject with custom properties without losing default ones Issue resolved!

Answer №2

The process of restoring the fabric Icon class is a bit more intricate compared to the fabric Image class because it requires an icon element in the constructor, which is not serializable.

This is the approach taken in fabricJS for implementing it:

  /**
   * Creates an instance of fabric.Icon from its object representation
   * @static
   * @param {Object} object Object to create an instance from
   * @param {Function} callback Callback to invoke when an icon instance is created
   */
  fabric.Icon.fromObject = function(_object, callback) {
    var object = fabric.util.object.clone(_object);
    fabric.util.loadImage(object.src, function(img, isError) {
      if (isError) {
        callback && callback(null, true);
        return;
      }
      fabric.Icon.prototype._initFilters.call(object, object.filters, function(filters) {
        object.filters = filters || [];
        fabric.Icon.prototype._initFilters.call(object, [object.resizeFilter], function(resizeFilters) {
          object.resizeFilter = resizeFilters[0];
          fabric.util.enlivenObjects([object.clipPath], function(enlivedProps) {
            object.clipPath = enlivedProps[0];
            var icon = new fabric.Icon(img, object);
            callback(icon, false);
          });
        });
      });
    }, null, object.crossOrigin);
  };

You will need to copy this code and replace fabric.Image with fabric.Icon.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Dealing with AJAX errors in React components after mounting

Facebook suggests that the optimal method for loading initial data in a React component is to execute an AJAX request within the componentDidMount function: https://facebook.github.io/react/tips/initial-ajax.html It would look something like this: ...

Refining an array data table within a nested component

Transitioning my old PHP/jquery single-page applications to VueJS/Webpack has been a journey I'm undertaking to familiarize myself with the latter technology. It involves converting a simple table that pulls data from a JSON API and incorporates filte ...

Resolving conflicting event handlers within vue.js

I have a situation where I'm trying to use two buttons on a page to navigate to different sections. When I include only one button, everything works fine. But when I include both buttons, only one of them functions properly. Upon debugging, I noticed ...

Employing the html.validationmessagefor to display a client-side error notification

My text fields have server-side validation messages. The field 'title' is a required field on the server side with a '[Required]' data attribute in the class, but it only seems to be checked on a postback or form submit. I am saving the ...

Importing JSON data from a URL to display in an HTML table

Looking to utilize the Untappd API for a beer menu project, I've encountered an issue. The goal is to showcase the JSON data received from the server and organize it into an HTML table. Despite successfully importing data from a local list.json file ...

Issues with image uploads in Node.js database storage

Trying to update an image using the put method is resulting in a 'filename' undefined error. Interestingly, the image updates successfully when editing without changing the image. The code snippet below shows the implementation: app.put('/a ...

Is it acceptable to debut a full-screen iframe widget compared to an embedded one?

My current project involves integrating a custom widget into users' websites for my application. I am considering using an iframe as it seems to be the most efficient option for several reasons: Utilizing the custom framework of the application will ...

Using es6 map to deconstruct an array inside an object and returning it

I am looking to optimize my code by returning a deconstructed array that only contains individual elements instead of nested arrays. const data = [ { title: 'amsterdam', components: [ { id: 1, name: 'yanick&a ...

Anticipate the completion of Subject callback execution

Take a look at the code snippet below: const mA = async () => { try { const subscription = myEmitter.subscribe(url => getD(url)); const la=()=>{...}; return la; } catch (error) { throw error; } }; ...

Initiating Internet Explorer using the APTool application for Selenium automation

Have you ever encountered a situation where you needed to open Internet Explorer from the start menu with the right-click option open with *apptool and then navigate to a specific webpage? I wonder, is it possible to automate this process using Selenium W ...

What is the most effective way to condense these if statements?

I've been working on a project that includes some if statements in the code. I was advised to make it more concise and efficient by doing it all in one line. While my current method is functional, I need to refactor it for approval. Can you assist me ...

What is the most effective method for structuring JSON data that is utilized by a single-page application (SPA)?

A colleague and I are collaborating on a single page application (built in React, but the framework used isn't crucial; the same query applies to Angular as well). We have a database with 2 interconnected tables: Feature Car Both tables are linked ...

Steps for Generating a CSV File with a Format Similar to the JSON File

I'm looking to update information for several scholars in an NFT game called Axie Infinity. This involves using a JSON file with a specific format: { "name": "Scholar 1", "ronin": "ronin:<account_s ...

Identifying the camera model using getMediaStream

Are there methods available to determine if a user's device has a front, rear, or dual cameras installed? For instance, laptops typically only have a front-facing camera while some devices may have both. I am looking for a way to identify the type of ...

Establish a timeout period for ajax requests using jQuery

$.ajax({ url: "test.html", error: function(){ //do something }, success: function(){ //do something } }); At times, the success function performs well, but sometimes it does not. How can I add a timeout to this ajax re ...

How to set up 'ng serve' command in Angular to automatically open a private browsing window?

I am looking for a way to open my project in an Incognito Mode browser without storing any cache. Is there a specific Angular CLI flag that can be included in the ng serve -o command or in the Angular CLI configuration file to enable opening a browser in ...

"Encountering difficulties while setting up an Angular project

I am currently working on setting up an Angular project from scratch. Here are the steps I have taken so far: First, I installed Node.js Then, I proceeded to install Angular CLI using the command: npm install -g @angular/cli@latest The versions of the ...

Steps for adding a row as the penultimate row in a table

There aren't many solutions out there that don't rely on jQuery, but I'm looking to avoid it. The row content needs to be generated dynamically. Here is my flawed approach: function addRow() { let newRow = '<tr><td>B ...

Passing parent props to child components in Vue.js - A comprehensive guide!

Trying to understand the process of passing a prop from parent to child components. If I include the prop attribute with the #id within the child component tag, like Image cid="488484-49544894-584854", it functions correctly. However, I am interested in u ...

I am looking to display data in Angular based on their corresponding ids

I am facing a scenario where I have two APIs with data containing similar ids but different values. The structure of the data is as follows: nights = { yearNo: 2014, monthNo: 7, countryId: 6162, countryNameGe: "რუსეთის ...