fusion-plugin-font-loader-react/src/font-loader.js (57 lines of code) (raw):
/** Copyright (c) 2018 Uber Technologies, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
/**
* Polyfill loosely based on zenfonts
* https://github.com/zengabor/zenfonts/
* Copyright (c) 2015 Gabor Lenard
*/
/* global document */
const testFonts = ['sans-serif', 'serif', 'monospace'];
// TODO(#3): allow override
const testText = 'π+[.—_]imW12/?';
// how long to wait for load before switching style to true font anyway
const timeout = 60000;
export default function(font: string) {
if (__BROWSER__ && document) {
// $FlowFixMe
return document.fonts && typeof document.fonts.load === 'function'
? document.fonts.load(`1em ${font}`) // native API requires size
: loadFontPolyfill(font);
}
}
function loadFontPolyfill(font): Promise<void> {
const testDivs = createTestDivs();
// $FlowFixMe
testDivs.forEach(div => (div.style.fontFamily = `${font}, ${div.testFont}`));
const waitUntil = Date.now() + timeout;
return new Promise(resolve => {
(function monitorWidth(delay) {
setTimeout(function() {
if (Date.now() >= waitUntil || hasFontLoaded(testDivs)) {
resolve();
cleanup(testDivs);
} else {
monitorWidth(Math.max(delay * 1.5, 500));
}
}, delay);
})(10);
});
}
function hasFontLoaded(testDivs) {
// Test whether n elements that were originally diverse system fonts are now all equal widths
return Boolean(
testDivs
.map(div => div.offsetWidth)
.reduce((width1, width2) => (width1 === width2 ? width1 : NaN))
);
}
function cleanup(testDivs) {
testDivs.forEach(div => {
const p = div.parentNode;
if (p) {
p.removeChild(div);
}
return true;
});
}
function createTestDivs() {
return testFonts.map((testFont, i) => {
const div = document.createElement('div');
// $FlowFixMe
div.testFont = testFont;
div.style.cssText = `position:absolute;top:-999px;left:${-9999 +
i * 100}px;visibility:hidden;
white-space:nowrap;font-size:20em;font-family:${testFont}`;
div.appendChild(document.createTextNode(testText));
if (document && document.body) {
document.body.appendChild(div);
}
return div;
});
}