I’m working on a Nextjs project where I need to load a custom font (Rasa
from Google Fonts) into a FabricJS canvas, but I’m encountering an issue where the font is not applied until after I interact with the canvas (e.g., clicking on any text).
Steps to Reproduce:
-
I am using the
webfontloader
library to load a Google Font dynamically when the component mounts. -
I initialize a FabricJS canvas using
fabricjs-react
. -
I try to render text (
IText
objects) onto the canvas after the font is loaded, applying a scale factor and some custom styling.
However, the font doesn’t seem to apply correctly to the text objects initially. The text is rendered with the default font, and only when I click on the text (or interact with the canvas) does it update and display with the correct font.
"use client";
import React, { useEffect, useState } from "react";
import { FabricJSCanvas, useFabricJSEditor } from "fabricjs-react";
import { ProductResponse } from "@/lib/store/slices/products/products.type";
import { useScaleFactor } from "@/hooks/useScaleFactor";
import { removePx } from "@/lib/utils/stringUtil";
import { IText } from "fabric";
const EditingArea = (
{ height, width, cardData }:
{ height: string; width: string; cardData: ProductResponse; }
) => {
const { editor, onReady } = useFabricJSEditor();
const { containerRef, scaleFactor } = useScaleFactor({ height: removePx(height) });
const cardHeight = removePx(height);
const cardWidth = removePx(width);
const [fontLoaded, setFontLoaded] = useState(false); // State to track font loading
const [textRendered, setTextRendered] = useState(false); // State to track if text is rendered
// Load the font with webfontloader
useEffect(() => {
if (typeof window !== "undefined") {
import("webfontloader").then((WebFontLoader) => {
WebFontLoader.load({
google: {
families: ['Rasa'], // You can add more fonts here if needed
},
active: function () {
console.log("Font loaded");
setFontLoaded(true); // Update state once font is loaded
},
inactive: function () {
console.error("Font failed to load");
}
});
});
}
}, []); // Only runs once when the component mounts
// Render text once the font is loaded and scale factor is ready
useEffect(() => {
if (fontLoaded && editor && cardData && scaleFactor) {
const canvas = editor.canvas;
canvas.clear(); // Clear the existing canvas
// Ensure that the canvas has been properly initialized
if (!canvas) {
console.error("Canvas is not initialized yet.");
return;
}
// Create text objects based on the `cardData` (assuming `cardData.texts` exists)
const fabricTextObjects = cardData.data.cards[0].content.map((__text) => {
return new IText(__text.text, {
left: removePx(__text.left) * scaleFactor, // Apply scale factor for positioning
top: removePx(__text.top) * scaleFactor,
fill: __text.color,
fontSize: removePx(__text.fontSize) * scaleFactor, // Apply scale factor for font size
fontFamily: __text.fontFamily || "Rasa", // Apply the loaded font
textAlign: __text.textAlign || "center", // Default to center alignment
});
}) || [];
// Add text objects to the canvas
if (fabricTextObjects.length > 0) {
canvas.add(...fabricTextObjects);
// Update coordinates and render the canvas
canvas.forEachObject((obj) => {
if (obj instanceof IText) {
obj.setCoords(); // Recalculate coordinates
}
});
canvas.renderAll(); // Render all objects
setTextRendered(true); // Set the textRendered state
}
}
}, [fontLoaded, editor, cardData, scaleFactor]); // Only runs when these dependencies change
return (
<>
<div ref={containerRef} className="h-full w-full">
{scaleFactor ? (
<div style={{ height: cardHeight * scaleFactor, width: cardWidth * scaleFactor }}>
<FabricJSCanvas className="bg-white h-full w-full" onReady={onReady} />
</div>
) : (<></>)}
</div>
{textRendered && (
<div>
<p>Text has been rendered!</p>
</div>
)}
</>
);
};
export default EditingArea;
What I Expect:
-
The font
Rasa
should be applied to theIText
objects when they are rendered on the FabricJS canvas. -
The canvas should render the text correctly with the custom font immediately, without needing to click on it.
What Happens:
-
The text is initially rendered with the fallback font.
-
When I interact with the text (click on it or trigger some other event), the font updates and the text is displayed correctly.
-
The font does not apply to the text objects until after an interaction with the canvas.
Troubleshooting I’ve Done:
-
I verified that
webfontloader
is correctly loading the font by logging to the console ("Font loaded"
). -
I ensured that
fontLoaded
is set totrue
before rendering any text. -
The
editor
andcanvas
are properly initialized before I attempt to add text objects. -
I tried clearing the canvas (
canvas.clear()
) before adding new text objects to avoid any leftover items from previous renders.
Questions:
-
Why is the font not being applied to the text objects initially?
-
Is there an issue with how I’m handling dynamic font loading and rendering text on the FabricJS canvas?
-
How can I ensure that the font is applied immediately without the need for any interaction (e.g., clicking) to trigger the font update?
Any help or suggestions would be greatly appreciated!