The positioning of images on the fabricjs canvas seems to be unreliable and inconsistent

When trying to place a series of 4 images at specified coordinates in fabricjs, I am facing inconsistencies with their placement upon page load. Refreshing the page usually resolves the issue, but I want to prevent this from happening altogether.

If anyone knows how to solve this problem, here is the code I am currently using:

var objects = [
    { type: "image", filename : "tv.png" , x: 688, y: 184, angle: 0, zIndex: 10 },
    { type: "image", filename : "polaroid.jpg" , x: 347, y: 515 },
    { type: "image", filename : "polaroid.jpg" , x: 138, y: 643 },
    { type: "image", filename : "polaroid.jpg" , x: 429, y: 803 },
    { type: "text", text: "Your favorite band", x: 1168, y: 1163, angle: -17, canvasRef: null,zIndex: 50 }
];

for (var i=0; i<objects.length;i++){

    var objRef = objects[i];

    switch(objRef.type){
        case 'image':
            var url = "img/" + objRef.filename;

            fabric.Image.fromURL(url, function(img) {
                var objRef = objects[curObject];
                img.set({
                    left: objRef.x,
                    top: objRef.y,
                    angle: objRef.angle,
                    hasBorders: false,
                    hasControls: false,
                    hasRotatingPoint: false,
                    lockMovementX: true,
                    lockMovementY: true
                });
                canvas.add(img).renderAll();
                img.moveTo(objRef.zIndex);
                curObject++;
                //canvas.setActiveObject(img);
            });

            break;

        case 'text':
            var text = objRef.text;
            var fabricText = new fabric.Text(text, {
                left: objRef.x,
                top: objRef.y,
                angle: objRef.angle,
                hasBorders: false,
                hasControls: false,
                hasRotatingPoint: false,
                lockMovementX: true,
                lockMovementY: true
            });
            objRef.canvasRef = fabricText;
            addTextListeners(fabricText);
            canvas.add(fabricText);
            break;
    }

}

Please note that this code does not execute until after window.ready, and all images that fabric needs to load onto the canvas have been preloaded using the imagesloaded plugin.

I have also tried delaying the loading of each image using setTimeout between loads (instead of a loop), and deferring the canvas.renderAll() until after the loop, but without success. I even attempted to re-position the items on screen after placing them. Below are images depicting both the correct and incorrect layouts respectively.

Answer №1

It appears that there may be a race condition occurring due to the images loading in varying sequences.

The use of the fabric.Image.fromURL() method with a callback function means that the inner function may not execute in the same order as it was called. Additionally, the incrementation of the curObject variable assumes sequential calling of the function. Furthermore, re-assignment of objRef to a different object before image loading could explain discrepancies in x/y/rotate values (utilizing values from other objects in the array).

Instead of setting attributes with img.set() post-image loading, consider utilizing the third parameter of the Image.fromURL() method to pass in your desired attributes:

for (var i=0; i<objects.length;i++){
  var objRef = objects[i];
  switch(objRef.type){
    case 'image':
      var url = "img/" + objRef.filename;
      fabric.Image.fromURL(url, function(img) {
        canvas.add(img).renderAll();
      }, {
        left: objRef.x,
        top: objRef.y,
        angle: objRef.angle,
        hasBorders: false,
        hasControls: false,
        hasRotatingPoint: false,
        lockMovementX: true,
        lockMovementY: true
      });
      break;

    case 'text':
      // ...
      break;
  }
}

Answer №2

The solution lies in utilizing async callbacks to handle the Alert message triggers effectively.

fabric.Image.fromURL('hulk.png', function (img) {
    alert('First call');
    canvas.add(img).renderAll();
    }, {
        id: 'hulkid',
        num: 1,
        left: 10,
        top: 10,
        angle: 0
    });

fabric.Image.fromURL('hulk.png', function (img) {
    alert('Second call');
    canvas.add(img).renderAll();
}, {
    id: 'hulkid',
    num: 2,
    left: 25,
    top: 25,
    angle: 0
});
alert('Final call');

Answer №3

After experimenting with the setTimeout function, I finally found a solution.

I decided to test it using a sample provided by MidnightLightning.

However, there seemed to be an issue with the image z-index in the canvas despite everything being set correctly. It appeared that the fabric.Image.fromURL callback function was sometimes waiting for the image to load, causing errors in the queue for drawing images.

To tackle this problem, I introduced some timeouts into the function like so:

var count_layer=0;
var image_src_list_array=['1.jpg', '2.jpg'];

function paint_layer(){
    console.log(image_src_list_array[count_layer]);

    if(image_src_list_array[count_layer]!=''){
                    
        fabric.Image.fromURL(
              image_src_list_array[count_layer],
              function(img){
                   console.log('rendering');
                   fabric_canvas.add(img).renderAll();
              }, {
                   id: count_layer+1,
                   left: ...,
                   top: ...,
                   angle: ...,}

        ); 

        count_layer++;
        setTimeout(paint_layer,3000);
    }
    
}

The result in the console log now appears as follows:

1.jpg
rendering
2.jpg
rendering

Prior to these adjustments, the output looked like:

1.jpg
2.jpg
rendering
rendering

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

What could be causing my table to appear multiple times in the HTML when using jQuery?

Using Jquery to dynamically return a list of products, render it as HTML, and show it on the page using $(selector).html(html), I've encountered an issue. When adding products to the cart too quickly, which triggers the rendering of the cart again, th ...

What advantages can be gained from having multiple package.json files within a single application?

Embarking on the journey of creating my inaugural full react web application entirely from scratch. Previously, I've mainly worked on assignments that were partially pre-made for me. While setting up my project, I couldn't help but notice that I ...

Align audio and video elements in HTML5 using JavaScript

I am facing a situation where I have two files - one is a video file without volume and the other is an audio file. I am trying to play both of these files using <audio> and <video> tags. My goal is to make sure that both files are ready to pla ...

Troubleshooting Next.js 14.1 Pre-rendering Issue: A Step-by-Step Guide

I just updated my Next.js from version 14.01 to 14.1 and encountered an error during the build process of my application. How can I resolve this issue? The error message reads as follows: Error occurred while prerendering page "/collections". For more inf ...

What is the best way to flatten object literal properties?

I have received an object from a legacy server that I need to restructure on the client-side using JavaScript, jQuery, or Underscore.js. Here is the original structure of the object: [ { "Id":{ "LValue":1, "Value":1 }, ...

Similar to AngularJS, jQuery also provides a powerful tool for submitting forms

Recently, I've delved into the world of angularjs and have been truly amazed by its capabilities so far. One thing that took me by surprise was the lack of a simple solution for sending AJAX requests using the $http service. After hours of searching G ...

Component experiencing issues with service or @Input functionality

I have been struggling with importing a service inside a component and encountering an issue where the Input from the service does not render anything in the template. Let's take a look at my entity: export interface PageState { step: string; } e ...

unexpected alteration of text sizing in mathjax within reveal.js presentations

Something strange is happening with the font size in my slides. The code for each slide is the same, but there is an unexpected change between the 3rd and 4th slide. I cannot figure out what is causing this discrepancy. Oddly enough, when I remove the tit ...

How can I work with numerous "Set-Cookie" fields in NextJS-getServerSideProps?

When working with getServerSideProps, I found a way to set multiple cookies on the client device. This is the code snippet that I used: https://i.stack.imgur.com/Kbv70.png ...

What is the method for determining the angle between two planes?

My question is about calculating the angle between two planes. Is it also possible to calculate the angle between two Object3D points like we do with planes? If you need a visual example, check out this fiddle: https://jsfiddle.net/rsu842v8/1/ const ...

Tips for positioning a highcharts pie chart and legend in the middle of a page

I'm struggling to center my highchart data in a node/react single page application. Currently, it appears off-center like this: https://i.stack.imgur.com/ccR6N.png The chart is floating to the left and I would like to have everything centered within ...

Tips for properly invoking an asynchronous function on every rerender of a component in Vue.js

Situation: An analysis module on a website that needs to display three different data tables, one at a time. Approach: The module is a component containing three buttons. Each button sets a variable which determines which table to render. Depending on the ...

Perform a toggle action on the first row when clicking within the current row using Jquery

I've been grappling with the idea of creating a function that can display and hide a comment field when a button is clicked. The challenge here is that there are multiple line items with their own comment boxes. I want to find a way to achieve this wi ...

Having trouble obtaining a GuildMember's displayName in Discord.js leads to a TypeError

I'm completely baffled by the situation here. My code is integrated within the Akairo Framework, yet the error seems to be pointing fingers at discord.js itself. Take a look at the error message below: /home/runner/guard/Listeners/automod/nicknames.js ...

Modify the appearance of an element within an array upon selection by comparing it with a separate array

In my code, there is an array called tagList that contains a list of objects. When one of these objects is clicked on, it gets added to another array named selectedTags. var selectedTags = []; export default class RegisterTags extends Component { con ...

Unleashing the power of JavaScript: Sharing arrays and data structures effortlessly

Currently, I am utilizing HTML & JavaScript on the client side and NodeJs for the server side of my project. Incorporated in my form are multiple radio buttons. When the user clicks on the submit button, my intention is to post the results to the server. ...

The process of authenticating route parameters in Nuxt

I'm having trouble validating route parameters in my page component using the following code: async validate({ params, store }) { await store.dispatch(types.VALIDATE_PARAMS_ASYNC, params.id) } And here's the corresponding code in the store: ...

Retrieving outcomes from a sequence of callback functions in Node.Js

I've been struggling to get my exports function in Node.Js / Express app to return the desired value after going through a series of callback functions. I've spent hours trying to fix it with no success. Can someone provide some guidance? Here is ...

Certain crucial happenings inhibit the ability of others to occur

Hey there! Want to check out the live application I'm currently working on? Simply click this link: turbo_synth This project is being developed using VueJS, however, the issue I've encountered does not seem to be related to Vue at all. The prob ...

Using jQuery, how can you make fixed elements fade as the page scrolls?

How can I make a fixed element, such as a corner ad or notice, fade when the page is scrolled down to a certain point? What would be the most effective method for determining this point: using pixels, percentage, or another way? And how can I implement th ...