Reputation: 1278
I'm on fabric 3.4 in a node environment. I am trying to load and create during the loading of an existing canvas.
The way it works, is I have an existing fabric stored as json, which has a rect
object in it that acts as a placeholder for an image. So when the canvas is loaded, and image location is provided so that it can replace the existing rect
object with the same size and position.
I think I'm pretty close, though any documentation for loading images without a url seems to be very outdated. These are local files, not available by any url.
import { fabric } from 'fabric';
const Canvas = require('canvas');
var canvasobj = new fabric.StaticCanvas(null, {
height: 1000,
width: 1000
});
let template = JSON.parse(data);//data is canvas json
canvasobj.loadFromJSON(template, () => {
canvasobj.renderAll();
},
(o, object) => {
if(object.stitch.type === 'portrait'){//identifier for placeholder
const c = Canvas.createCanvas(2304, 3456)
const ctx = c.getContext('2d')
Canvas.loadImage(binds.image).then((image) => {
ctx.drawImage(image, 50, 0, 70, 70)
fabric.Image.fromURL(c.toDataURL(), (img) => {
canvasobj.add(img)
canvasobj.remove(object);//remove the placeholder
}
}
}
}
...
I think this method will work. But the problem I'm having is Canvas.loadImage
is asynchronous, and this reviver that runs on each object is not. The revivers get run, and then the callback that renders the canvas gets called before the image has loaded. If I make the reviver async
and await
the image loading, this dons't change anything, since nothing is waiting for the reviver to resolve.
Is there a way to get this method to work? Is there a different way to approach this?
Upvotes: 1
Views: 2832
Reputation: 5317
You can create an async function from a callback based method using a Promise.
async function fabricImageFromURL(image_url) {
return new Promise(function(resolve, reject) {
try {
fabric.Image.fromURL(image_url, function (image) {
resolve(image);
});
} catch (error) {
reject(error);
}
});
}
then you can use it in your async
function using await
:
async function doingMyStuff() {
const image = await fabricImageFromURL(image_url);
// here image is already loaded
}
Upvotes: 3
Reputation: 4988
If your only issue is that you need to wait for all the revivers, you can just create an empty array and push a new Promise
each time the reviver is called. Resolve each of those promises when your async image load is done. In the meantime, call Promise.all()
and wait for the whole async stuff to end. The revivers themselves seem to be called synchronously, so by the time your script reaches Promise.all()
, you'll have all those promises in the Pending
state.
const promiseArray = []
canvasobj.loadFromJSON(template, () => {},
(o, object) => {
const p = new Promise((resolve) => {
//...
Canvas.loadImage(binds.image).then((image) => {
//...
fabric.Image.fromURL(c.toDataURL(), (img) => {
//...
resolve()
})
})
})
promiseArray.push(p)
})
Promise.all(promiseArray)
.then(() => {
// ...
})
Upvotes: 2