admin管理员组

文章数量:1425761

I have a series of 4 images which I'm attempting to place at specified coordinates in fabricjs, and I'm getting them placed inconsistently sometimes when the page loads. Refreshing once or twice will usually give the correct layout. Trying to prevent this from happening.

Anybody know how to solve this? Here's my code:

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;
    }

}

I should mention also that none of this code happens until after window.ready, and after all the images that fabric is attempting to load to canvas have been preloaded using the imagesloaded plugin.

I should also mention that I've tried delaying the loading of each successive image using setTimeout between each load (instead of a loop), and saving the canvas.renderAll() until after the loop, with no success. Even tried running a loop to re-position the items after they were placed on screen. Below are images of the issue - correct, and incorrect, respectively.

I have a series of 4 images which I'm attempting to place at specified coordinates in fabricjs, and I'm getting them placed inconsistently sometimes when the page loads. Refreshing once or twice will usually give the correct layout. Trying to prevent this from happening.

Anybody know how to solve this? Here's my code:

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;
    }

}

I should mention also that none of this code happens until after window.ready, and after all the images that fabric is attempting to load to canvas have been preloaded using the imagesloaded plugin.

I should also mention that I've tried delaying the loading of each successive image using setTimeout between each load (instead of a loop), and saving the canvas.renderAll() until after the loop, with no success. Even tried running a loop to re-position the items after they were placed on screen. Below are images of the issue - correct, and incorrect, respectively.

Share Improve this question edited Apr 17, 2014 at 15:25 mheavers asked Apr 17, 2014 at 14:13 mheaversmheavers 30.2k63 gold badges201 silver badges326 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

It looks like you have a race condition from the images loading in different orders.

The fabric.Image.fromURL() method is using a callback function, which fires after the image has loaded, so you're not guaranteed to have that inner function fire in the same order you called it. But you're using curObject to increment upwards each time the function is called, which assumes they're called in order. And the objRef can get re-assigned to a new object by the time the image loads, which would be why the x/y/rotate values are off (using the values from the other objects in the array).

So, instead of using img.set() after the image had loaded, use the third parameter of the Image.fromURL() method to pass in your 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;
  }
}

So the key to the answer is async call back. Just check when the Alert message called out.

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

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

I found out the way with setTimeout function.

I tested with MidnightLightning's sample

But still has some problem in image z-index in canvas(all settings are good). I think sometimes fabric.Image.fromURL callback function is waiting the image loading, so there would some mistakes in queue of drawing image.

So I added some timeout to function, like that.

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);
    }
    
}

Then the console log result is:

1.jpg
rendering
2.jpg
rendering

Before it outputs

1.jpg
2.jpg
rendering
rendering

本文标签: javascriptfabricjs placement of images on canvas inconsistentStack Overflow