I have a strange bug. I have a React app that is using fabricjs. I can render things onto the canvas just fine, but when I try to save the canvas as an image, a strange bug occurs: after canvas.toDataURL is called, the entire canvas goes blank until the user clicks on one of the elements on the canvas itself, after which all elements will reappear.
The url created by toDataURL works fine: downloading this file will produce the expected image with all of the elements rendered
Here is a fiddle that demonstrates this behavior:
https://jsfiddle.net/db1gc45p/22/
const {useState, useEffect, useRef, useCallback} = React;
const useFabric = (onChange) => {
const fabricRef = useRef();
const disposeRef = useRef();
return useCallback(
(node) => {
if (node) {
fabricRef.current = new fabric.Canvas(node);
if (onChange) {
disposeRef.current = onChange(fabricRef.current);
}
} else if (fabricRef.current) {
fabricRef.current.dispose();
if (disposeRef.current) {
disposeRef.current();
disposeRef.current = undefined;
}
}
},
[onChange]
);
};
function Example() {
const ref = useFabric((canvas) => {
canvas.add(new fabric.Text("hello"))
canvas.toDataURL()
});
return (
<div style={{ display: "inline-block", border: "solid" }}>
<canvas
ref={ref}
width={100}
height={100}
/>
</div>
);
}
ReactDOM.render( <Example />, document.getElementById('root') );
You can see the bug for yourself. By commenting/uncommenting line 30 canvas.toDataURL()
, you can see that the canvas element stays visible when we don't call canvas.toDataURL()
However, even if we do call canvas.toDataURL()
, we can get the element to re-appear by clicking on it on the canvas.
How can I prevent all of the elements from going invisible?
Fabric.js does some rendering updates asynchronously, similar issue, however you can force a synchronous update (after doing something that triggers the async update) by using canvas.renderAll().
Try
function Example() {
const ref = useFabric((canvas) => {
canvas.add(new fabric.Text("hello"))
canvas.renderAll()
canvas.toDataURL()
});