Utilizing FabricJs to import and export dynamic patterns via JSON (patternSourceCanvas)

I'm facing an issue with saving and loading dynamic patterns applied to objects. I've tried to find a solution online without success. I understand the reason behind the problem but I'm unsure how to fix it.

This is basically what I'm doing:

  1. Applying dynamic pattern to object.
  2. Saving canvas to MongoDB using 'JSON.stringify(canvas.toJSON([...])'.
  3. Loading canvas using 'loadFromJSON'.
  4. Encountering error 'Uncaught ReferenceError: patternSourceCanvas is not defined'.

All the information I've come across on this problem dates back at least 2 years ago, some even from 2013, with no concrete working example with code.

UPDATE

Below is the function I use to apply patterns on paths:

function applyPatternOnPath(p, image, width, patternRepeat, patternPadding) {

    if (patternRepeat) {
      var r = 'repeat'
    } else {
      var r = 'no-repeat'
    }

    fabric.Image.fromURL(image, function(img) {

      var padding = 0 + patternPadding;

      img.scaleToWidth(width);

      var patternSourceCanvas = new fabric.StaticCanvas();

      patternSourceCanvas.add(img);
      patternSourceCanvas.renderAll();

      var pattern = new fabric.Pattern({
        source: function() {
          patternSourceCanvas.setDimensions({
            width: img.getScaledWidth() + padding,
            height: img.getScaledHeight() + padding
          });
          patternSourceCanvas.renderAll();
          return patternSourceCanvas.getElement();
        },
        repeat: r
      });

      p.set('fill', pattern);
      canvas.renderAll();

    }, { crossOrigin: 'Anonymous' });
  }

Answer №1

I was able to resolve my issue by implementing a creative workaround. Although I am unsure if it is considered the most traditional method for handling dynamic patterns saved with JSON, it has proven effective for my needs.

This is my approach...

  1. First, I apply the dynamic pattern to an object.
  2. Before saving the canvas (JSON string) to MongoDB, I take the following steps...

    a) I store the object's information (including pattern src, width, padding, etc.) within a field of the MongoDB document named 'canvasLayers'.

    b) I clear all 'fill' properties of paths with dynamic patterns by setting the 'fill' property to "".

    As a result, the JSON saved to the DB does not contain any pattern information.

  3. When loading a previously saved canvas, I reapply the patterns based on the information stored in the 'canvasLayers' field for each object.

In essence, I opt not to save the pattern information within the JSON string but rather store it in a separate object (MongoDB field), reintroducing the patterns upon canvas load.

Answer №2

Here's a code snippet to convert an image source into base 64 format and store it for later use:

// Customization of fabric.Pattern to convert image source
var toFixed = fabric.util.toFixed;
fabric.Pattern.prototype.toObject = function(propertiesToInclude) {
  var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
    source, object;
  if (typeof this.source === "function") {
    source = String(this.source);
  } else if (typeof this.source.src === "string") {
    source = this.source.src;
  } else if (typeof this.source === "object" && this.source.toDataURL) {
    source = this.source.toDataURL();
  }
  object = {
    type: "pattern",
    source: source,
    repeat: this.repeat,
    crossOrigin: this.crossOrigin,
    offsetX: toFixed(this.offsetX, NUM_FRACTION_DIGITS),
    offsetY: toFixed(this.offsetY, NUM_FRACTION_DIGITS),
    patternTransform: this.patternTransform ? this.patternTransform.concat() : null
  };
  fabric.util.populateWithProperties(this, object, propertiesToInclude);
  return object;
};



var imageUrl = 'https://upload.wikimedia.org/wikipedia/commons/2/22/Wikimapia_logotype.svg';
var canvas = new fabric.Canvas('canvas');
var rect = new fabric.Rect({
  width: 200,
  height: 200,
  strokeWidth: 2,
  stroke: '#000'
})
canvas.add(rect);

fabric.Image.fromURL(imageUrl, function(img) {
  //alert('t' + img);
  console.log('img', img);
  img.scaleToHeight(200);
  var patternSourceCanvas = new fabric.StaticCanvas();
  patternSourceCanvas.add(img);
  patternSourceCanvas.setDimensions({
    width: img.getWidth(),
    height: img.getHeight()
  });
  patternSourceCanvas.renderAll();
  var pattern = new fabric.Pattern({
    source: patternSourceCanvas.getElement()
  });
  rect.fill = pattern;
  canvas.renderAll();
}, {
  crossOrigin: 'annonymous'
});

$('#loadjson').on('click', function() {
  var json = canvas.toJSON();
  console.log('json', json['objects']);
  canvas.clear();
  setTimeout(function() {
    canvas.loadFromJSON(json, canvas.renderAll.bind(canvas));
  }, 3000)
})

CSS:

canvas{
  border:2px solid #000;
}

HTML:

<canvas id="canvas" width="300" height="300"></canvas><br>
<button  id="loadjson">loadfromjson </button>


<script src='https://www.multicastr.com/imageeditor/assets/js/fabric.unmin.js'></script>

<script src="https://www.multicastr.com/user/js/jquery.min.js"></script>

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

Error encountered when initializing NextJS Firebase Authentication App

I'm encountering challenges with implementing Firebase authentication using Google Provider in NextJS. I have set up the necessary environment variables and successfully established a connection to Firebase. However, I'm running into an issue whe ...

Combining data from two MongoDB collections with JSON arrays

I have experience writing queries for simple single key-value joins using $lookup, but I am facing a more complex scenario now and unsure how to tackle it. product : { "_id": ObjectId("6200a77598412e443c03f0ee"), "name&quo ...

Your search parameter is not formatted correctly

I am currently working on filtering a collection based on different fields such as name by extracting the values from the URL parameters. For example: http://localhost:3000/patient?filter=name:jack I have implemented a method to retrieve and convert these ...

The function of cookieParser() is causing confusion

Having an issue that I've been searching for answers to without success. When using app.use(express.cookieParser('Secret'));, how can we ensure that the 'Secret' is truly kept secret? I'm feeling a bit lost on this topic. Is ...

An expected expression was encountered near the if condition

I am encountering an expression expected error in Visual Studio near if(isNullOr ........ if (value) { if (isNullOrUndefined(x.value) && isNullOrUndefined(x.value2)) { x.minMark + '-' + a + '*' + x.b + ' ' + ...

Is it possible to transfer files using web-bluetooth technology?

As I work on developing an embedded system that counts the number of cars, saves their speed and time data in a logs file using rsyslog. Simultaneously, I am creating a web-API (in Typescript/Angular with Electron for Desktop usage and later Web as well) t ...

Customizing AngularJS directives by setting CSS classes, including a default option if none are specified

I have designed a custom directive that generates an "upload button". This button is styled with bootstrap button CSS as shown below: <div class="btn btn-primary btn-upload" ng-click="openModal()"> <i class="fa fa-upload"></i> Upload & ...

Retrieving the chosen option in Vue.js when the @change event occurs

I have a dropdown menu and I want to perform different actions depending on the selected option. I am using a separate vue.html and TypeScript file. Here is my code snippet: <select name="LeaveType" @change="onChange()" class="f ...

Leveraging the results from a static React function

I am currently working on a React + Webpack project that supports JavaScript ECMAScript 6. Here is the code snippet I am trying to implement: class ApiCalls extends React.Component{ static uploadFiles(files) { // upload code if(success) { ...

Sequencing requests and processing data in Node.js through event handling

Is there a way to combine the responses from two requests into one single JSON response? The goal is to have an array containing both {response1JSON} and {response2JSON}, with each response streaming data that needs to be read. function getSongs() { c ...

Creating a square shape in Twitter Bootstrap to frame an item on a webpage

I've been working on creating a webpage that looks similar to the image provided. I've managed to get about 90% of it done, but there are a couple of issues I'm facing: How can I create a square with a triangle at the bottom as shown in th ...

Delete one object and then sequentially rename all remaining objects

object This is the object I retrieved. How can I remove module_1 object and rename the module object? For example, remove module_1 and rename module_2, module_3... to module_1, module_2... `{ "module_1": { "modulename": "mat ...

Next.js version 13 is causing the page to refresh each time the router is pushed

I am currently developing a search application using NextJs 13, and I have encountered an issue where the page refreshes every time I click the search button. Strangely, this only happens when the application is deployed on Vercel. When running the app l ...

What is the best way to concatenate a data object?

This task should be quite straightforward. Using Vanilla JS, I am trying to update the content of a span element with the session ID obtained from a function call. Here's an example: sessionId = 0_77b1f7b5-b6c8-49a0-adbc-7883d662ebba document.getEle ...

AngularJS - UI Bootstrap: Easily expand or collapse all items in the Accordion widget

I have created a code to open and close all tabs of an accordion individually using separate 'open' and 'close' buttons. However, it requires me to dynamically add a key value pair (a Boolean value) to my JSON data. What is the best ap ...

Can an onload function be triggered within the location.href command?

Can a function be called onload in the location.href using jQuery? location.href = getContextPath() + "/home/returnSeachResult?search=" + $('#id-search-text-box').val() + "&category=" + $('#search_concept').text() + "onload='j ...

Elements recognized worldwide, Typescript, and a glitch specific to Safari?

Consider a scenario where you have a select element structured like this: <select id="Stooge" name="Stooge"> <option value="0">Moe</option> <option value="1">Larry</option> <option value="2">Curly</option ...

rxjs iterates through an array executing each item in sequential order

Is there a way to make observables wait until the previous one has completed when they are created from an array? Any help is appreciated! export class AppComponent{ arr: number[] = [5, 4, 1, 2, 3]; fetchWithObs() { from(this.arr) ...

JavaScript for varying content that is dynamically loaded on a completely ajax-powered website

This post has been updated to address the issue more effectively with a refined concept and code (based on the responses provided so far) I am working on developing an ajax-driven website, but I have encountered some issues with multiple bound events. He ...

Encountering an issue with the `className` prop not matching when deploying to Heroku, yet the functionality works perfectly when testing locally

I encountered this specific error message: The className property did not match. On the server: "jss1 jss5" Client side: "makeStyles-root-1 makeStyles-root-5" This issue only arises when deploying to Heroku. Locally, everything runs ...