Pomax / lib-font

This library adds a new Font() object to the JavaScript toolbox, similar to new Image() for images
MIT License
731 stars 72 forks source link

Questions about asynchronous font loading #33

Closed dandelany closed 4 years ago

dandelany commented 8 years ago

EDIT: Sorry, please disregard this comment and see the next comment

Hi Pomax,

Thanks for the cool library - I've been looking for a way to measure text before rendering to the DOM for ages, and it looks like this may be it!

However, I am having an issue with measuring system fonts which are already loaded. Specifically, it seems like setting the src property on the font kicks off an asynchronous process, but there is no way to know when it is done and ready to measure. So if I run the following code:

var font = new window.Font();
font.fontFamily = "Helvetica Neue";
font.onload(() => { console.log('loaded'); });
font.src = font.fontFamily;
console.log(font.measureText('ok', 12));

It logs false, and never logs "loaded". The only way I have gotten it to actually work with system fonts is to use setTimeout, eg. this works fine:

setTimeout(() => console.log(f.measureText('ok', 12)), 1000);

However, this is unfortunately pretty untenable for use in my application code. Is there another callback I can use, or any way to make this work synchronously? Thanks for taking a look.

dandelany commented 8 years ago

Nevermind, sorry! I was doing font.onload(callback) instead of font.onload = callback as I should have been doing. Onload is indeed called correctly for system fonts.

However, now I'll hijack this issue to ask a different question: Currently there is a 10ms wait before system fonts are processed. But the processing step itself also contains a 50ms timeout delay - which seems like it should obviate the need for the first delay. Are they both truly necessary or can the first 10ms delay be omitted? My limited testing in Chrome shows no problems with processing it immediately.

dandelany commented 8 years ago

And one more related question: The delayedValidate function is currently called with a setTimeout of 50ms in order to give the browser time to finish rendering/painting. This seems like it would a good use case for requestAnimationFrame which should be called when the repaint is finished.

I tried plugging it in and it seems to work fine in latest versions of Chrome, Firefox and Opera, though I haven't tested extensively. Would this be a good improvement, or are there good reasons not to use RAF here? I can submit a PR if you'd like.

Pomax commented 8 years ago

Hmm, honestly at this point you're probably going to get better mileage out of the opentype.js linked to from the README.md. It shouldn't have any artificial delays I had to use back when I made this library.

dandelany commented 8 years ago

Unfortunately, it seems that Opentype does not support system fonts, nor does it have a font.measureText equivalent.

I pretty much only have a single use case: get the bounding box of a string with a given (already loaded) font & size, before rendering to the DOM - this way you can do more interesting layout things faster. Otherwise you need to add to the DOM, measure, then remove, which is slow.

It seems like this is not a well-covered case by existing libraries. I may consider forking Font.js or Opentype.js to create a library that does only this.

Pomax commented 8 years ago

that's true, it won't do system font metrics since it shapes based on the actual font file. Both the 10ms and 50ms delays are mostly to move over to the next tick without immediately hijacking the thread again. Compared to the time necessary to do the canvas drawing and measuring, the combined 60ms (~4 frames if we're thinking in animation timing) is pretty much insignificant, but I didn't want to use 0ms or 1ms timeout values. Immediate processing will do terrible things if you're also loading other scripts at the same time, so you always want to bake in a few "breathing spaces" in long scripts to make sure it doesn't lock the page. I know requestAnimationFrame exists, but I don't use it for anything except literally that: animation draw loops.

Pomax commented 4 years ago

obsolete with the 2019 rewrite