1

I want to render some text in canvas using a particular font. But the text is drawn before text font is loaded.

I've tried to use document.fonts.ready.then() but apparently has no effect

This is at first load

enter image description here

This is when reloading (font in cache)

enter image description here

See this fiddle

HTML

<canvas id="my_canvas"></canvas>

JS

function draw()
{
  var canvas = document.getElementById("my_canvas");
  var ctx = canvas.getContext("2d");

  ctx.canvas.width = 150;
  ctx.canvas.height = 150;

  ctx.fillStyle = "red";
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  ctx.font = "40px Special Elite";   
  ctx.fillStyle = "white";   
  ctx.fillText("hello", 25, 50);
}

document.fonts.ready.then(function()
{
    draw();
});

CSS

@import url('https://fonts.googleapis.com/css2?family=Special+Elite');

I'm also trying locally with this css, but I have the same results

@font-face
{
    font-family: "custom_font";
    src: url("some_font_file.ttf");
}

How can I draw the text only once the font is loaded?

Matías Cánepa
  • 5,770
  • 4
  • 57
  • 97
  • Did any of the solutions in [this question](https://stackoverflow.com/questions/4712242/wait-for-fonts-to-load-before-rendering-web-page) help you? Or any of [these](https://stackoverflow.com/search?q=wait+for+font+to+load)? – freedomn-m Oct 21 '22 at 17:11
  • 1
    The problem with your current code is that the font isn't used anywhere before `draw()` is being called. So the browser didn't put it in the *list of resources that need to be dowloaded before `document.fonts.ready` resolves*. You need to explicitly tell the browser to load that font. – Kaiido Oct 22 '22 at 02:30

2 Answers2

0

Instead, load the font file in the head tag (don't forget to remove the @import line):

<link rel="preconnect" href="https://fonts.gstatic.com/s/specialelite/v18/XLYgIZbkc4JPUL5CVArUVL0ntnAOSA.woff2" crossorigin>
Shoaib Amin
  • 207
  • 1
  • 4
0

You can use a custom async font loader method like this:

let fonts = [
  {
    "font-family": "Special Elite",
    "font-style": "normal",
    "font-weight": 400,
    src:
      "https://fonts.gstatic.com/s/specialelite/v18/XLYgIZbkc4JPUL5CVArUVL0ntnAOSA.woff2"
  }
];

drawToCanvas();

async function drawToCanvas() {
  var canvas = document.getElementById("my_canvas");
  var ctx = canvas.getContext("2d");

  //load fonts 
  await loadFonts(fonts);

  ctx.canvas.width = 150;
  ctx.canvas.height = 150;
  ctx.fillStyle = "red";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.font = "40px Special Elite";
  ctx.fillStyle = "white";
  ctx.fillText("hello", 25, 50);
}



async function loadFonts(fontsToLoad) {
  if (fontsToLoad.length) {
    for (let i = 0; i < fontsToLoad.length; i++) {
      let fontProps = fontsToLoad[i];
      let fontFamily = fontProps["font-family"];
      let fontWeight = fontProps["font-weight"];
      let fontStyle = fontProps["font-style"];
      let fontUrl = Array.isArray(fontProps["src"])
        ? fontProps["src"][0][0]
        : fontProps["src"];
      if (fontUrl.indexOf("url(") === -1) {
        fontUrl = "url(" + fontUrl + ")";
      }
      let fontFormat = fontProps["src"][0][1] ? fontProps["src"][1] : "";
      const font = new FontFace(fontFamily, fontUrl);
      font.weight = fontWeight;
      font.style = fontStyle;
      await font.load();
      document.fonts.add(font);
      console.log(fontFamily, "loaded");

      // apply font styles to body
      let fontDOMEl = document.createElement("div");
      fontDOMEl.textContent = "";
      document.body.appendChild(fontDOMEl);
      fontDOMEl.setAttribute(
        "style",
        `position:fixed; height:0; width:0; overflow:hidden; font-family:${fontFamily}; font-weight:${fontWeight}; font-style:${fontStyle}`
      );
    }
  }
}
<canvas id="my_canvas"></canvas>

Open the google font css URL to get the actual font file
https://fonts.googleapis.com/css2?family=Special+Elite
will return something like this (depending on your browser)

/* latin */
@font-face {
  font-family: 'Special Elite';
  font-style: normal;
  font-weight: 400;
  src: url(https://fonts.gstatic.com/s/specialelite/v18/XLYgIZbkc4JPUL5CVArUVL0ntnAOSA.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Copy the src to the fonts object array (you can add multiple fonts):

let fonts = [
  {
    "font-family": "Special Elite",
    "font-style": "normal",
    "font-weight": 400,
    src:
      "https://fonts.gstatic.com/s/specialelite/v18/XLYgIZbkc4JPUL5CVArUVL0ntnAOSA.woff2"
  }
];

Make you drawing function asynchronous

async function drawToCanvas() {
  var canvas = document.getElementById("my_canvas");
  var ctx = canvas.getContext("2d");

  // wait for fonts 
  await loadFonts(fonts);

  ctx.canvas.width = 150;
  ctx.canvas.height = 150;
  ctx.fillStyle = "red";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.font = "40px Special Elite";
  ctx.fillStyle = "white";
  ctx.fillText("hello", 25, 50);
}
herrstrietzel
  • 11,541
  • 2
  • 12
  • 34