Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas only renders custom webfont on click event with fabric.js

I have an ongoing problem with custom webfonts and fabric.js. My app uses a lot of custom webfonts and I init them when adding an iText to my canvas:

var text = new fabric.IText("My Text", {
    fontFamily: "Some Custom Font Family",
    fontSize: 50,
    top: 0,
    left: 0,
    fill: "#000000"
  });

  canvas.add(text);
  canvas.bringToFront(text);
  canvas.setActiveObject(text);

  canvas.renderAll();

That works but only if I click on the iText on my canvas and interact with it. Then, once the font has loaded, its not a problem anymore. The problem is initially and the first time the iText is added.

I researched a lot and came to this thread:

Init loaded text with remote web font in Fabric.js

but that didn't help me. The jsfiddle provided there has the exact same problem:

http://jsfiddle.net/vvL6f/6/

Just open this fiddle with a fresh browser (e.g. Chrome CMD+Shift+R) with cleared cache. You will see once you open the fiddle, the custom webfont is not loaded but immediately loads when you click on the iText on the right.

Now, how can we solve this?

A suggested approach was to set useNative to false and let Cufon render the text, but that didn't work.

I load my webfonts in a CSS file like this:

@font-face {
    font-family: 'ApolloASM';
    src: url('ApolloASM-webfont.eot');
    src: url('ApolloASM-webfont.eot?#iefix') format('embedded-opentype'),
         url('ApolloASM-webfont.woff2') format('woff2'),
         url('ApolloASM-webfont.woff') format('woff'),
         url('ApolloASM-webfont.ttf') format('truetype'),
         url('ApolloASM-webfont.svg#apollo_asmregular') format('svg');
    font-weight: normal;
    font-style: normal;
}
like image 735
DonMB Avatar asked Mar 05 '15 11:03

DonMB


1 Answers

@Prominic answer is almost there. I also have a canvas app which uses custom fonts with Fabric. I load all fonts in a CSS just as you do. The catch is, only declaring the @font-face in your stylesheet is not enough. The browser won't request the font file unless it is used in the document. And you have to be sure to load and apply the webfont before initializing the canvas. This can be achievable with a little bit of work, which I'll illustrate with an example below. The downside is that you might need to preload all fonts that your app make available to your users.


  1. Declare your webfont

    Add a @font-face statement in a stylesheet. Just make sure it is loaded in the <head> section. Let's use your example:
 @font-face {
    font-family: 'ApolloASM';
    src: url('ApolloASM-webfont.eot');
    src: url('ApolloASM-webfont.eot?#iefix') format('embedded-opentype'),
         url('ApolloASM-webfont.woff2') format('woff2'),
         url('ApolloASM-webfont.woff') format('woff'),
         url('ApolloASM-webfont.ttf') format('truetype'),
         url('ApolloASM-webfont.svg#apollo_asmregular') format('svg');
    font-weight: normal;
    font-style: normal;
}

Bonus Tip: If you use italic and/or bold in your app, is adviseable to add the font-faces for those styles as weel if you want consistent results across different browsers. For example, Android Browser 4.2 can't compute italic of a font, and will display it as normal.

  1. Force the request for the font file

As I said above, you will need to actually use the font in the DOM to force the font to load. Just adding a div with some text and styling it with your font will be enough:

<div style="font-family:'ApolloASM'">&nbsp;</div>

Bonus Tip: Don't forget to add the italic and bold variations as well

  1. Init the FabricJS Canvas after the DOM load event

You will need to sync the canvas init (or at least the text addition) after the font is loaded by the browser, or else you might see a FOUT effect (Flash of Unstyled Text). Luckily for us, the browser won't fire the load event until all font files are loaded, which can be used to safely add a styled text to canvas:

window.addEventListener('load', function onLoad() {
    var canvas = new Fabric.Canvas({...});
    var text = new fabric.Text("Web Font Example", {
        left: 200,
        top: 30,
        fontFamily: 'ApolloASM',
        fill: '#000',
        fontSize: 60
    });

    canvas.add(text);
    canvas.renderAll();
})

Bonus Tip: You can wrap those fonts <div>'s in another one and remove it after the DOM load event, to avoid breaking any layout you might have.

like image 78
Bernardo Domingues Avatar answered Sep 19 '22 17:09

Bernardo Domingues