Created
December 17, 2019 16:40
-
-
Save jseppi/129e8d1b9c7cc43f2efdd3e7bded0d4f to your computer and use it in GitHub Desktop.
Render font previews // source https://jsbin.com/lomovel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title>Render font previews</title> | |
<meta | |
name="viewport" | |
content="initial-scale=1,maximum-scale=1,user-scalable=no" | |
/> | |
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.js"></script> | |
<link | |
href="https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.css" | |
rel="stylesheet" | |
/> | |
<link | |
href="https://api.mapbox.com/mapbox-assembly/v0.24.0/assembly.min.css" | |
rel="stylesheet" | |
/> | |
<script | |
async | |
defer | |
src="https://api.mapbox.com/mapbox-assembly/v0.24.0/assembly.js" | |
></script> | |
<style> | |
body { | |
margin: 0; | |
padding: 0; | |
background: lightblue; | |
} | |
.preview-image { | |
border: 1px solid black; | |
height:60px; | |
display:block; | |
margin-top: 10px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="render_map_1" style="position:absolute;top:-200px;width:800px; height: 120px; visibility: hidden;" class="border"></div> | |
<script id="jsbin-javascript"> | |
'use strict'; | |
function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); } | |
mapboxgl.accessToken = "pk.eyJ1IjoianNlcHBpbWJ4IiwiYSI6ImNqbGU1ODdtMzBpZjUzcG1pMWJnaHB2aHgifQ.xGVwKUpyJ-S5iyaLq7GFLA"; | |
var owner = 'jseppimbx'; | |
var renderMap1 = new mapboxgl.Map({ | |
container: 'render_map_1', | |
center: [0.525, 0], // TODO: real math? | |
zoom: 9, | |
pitch: 0, | |
bearing: 0, | |
fadeDuration: 0, | |
preserveDrawingBuffer: true, | |
localIdeographFontFamily: false | |
}); | |
var renderMap1Busy = false; | |
function generateFontPreviewStyle(_ref) { | |
var owner = _ref.owner; | |
var name = _ref.name; | |
var text = _ref.text; | |
var color = _ref.color; | |
return { | |
version: 8, | |
glyphs: 'mapbox://fonts/' + owner + '/{fontstack}/{range}.pbf', | |
sources: { | |
font: { | |
type: "geojson", | |
data: { | |
type: "FeatureCollection", | |
features: [{ | |
type: "Feature", | |
geometry: { | |
type: "Point", | |
coordinates: [0, 0] | |
}, | |
properties: {} | |
}] | |
} | |
} | |
}, | |
layers: [{ | |
id: "preview", | |
source: "font", | |
type: "symbol", | |
layout: { | |
"text-justify": "left", | |
"text-anchor": "left", | |
"text-field": text, | |
"text-font": [name], | |
"text-max-width": 200, | |
"text-size": 80, | |
"text-line-height": 5 // TODO: does this do anything? | |
}, | |
paint: { | |
"text-color": color, | |
"text-halo-color": color, | |
"text-halo-width": 0.3, | |
"text-halo-blur": 0 | |
} | |
}, { | |
// Show a circle here for easier | |
// positioning debugging | |
id: "markers", | |
source: "font", | |
type: "circle", | |
layout: {}, | |
paint: { | |
"circle-radius": 5, | |
"circle-color": "yellow", | |
"circle-stroke-width": 0 | |
} | |
}] | |
}; | |
} | |
// ***** redux-like stuff | |
var state = {}; | |
state.fontPreviewsToRender = new Set(); | |
state.renderedFontPreviews = {}; | |
function makeFontKey(name, color, text) { | |
return name + '^' + color + '^' + text; | |
} | |
function fontKeyToParams(key) { | |
var parts = key.split('^'); | |
return { | |
name: parts[0], | |
color: parts[1], | |
text: parts[2] | |
}; | |
} | |
function addFontPreviewToRender(name, color, text) { | |
var key = makeFontKey(name, color, text); | |
if (!state.fontPreviewsToRender.has(key)) { | |
state.fontPreviewsToRender.add(key); | |
} | |
} | |
function getFontPreviewsToRender() { | |
var paramsToRender = []; | |
state.fontPreviewsToRender.forEach(function (key) { | |
var params = fontKeyToParams(key); | |
paramsToRender.push(params); | |
}); | |
return paramsToRender; | |
} | |
function saveRenderedFontPreviewData(name, color, text, dataURL) { | |
var key = makeFontKey(name, color, text); | |
state.renderedFontPreviews[key] = dataURL; | |
if (state.fontPreviewsToRender.has(key)) { | |
state.fontPreviewsToRender['delete'](key); | |
} | |
} | |
// ***** fake output helpers | |
function addImage(dataURL) { | |
var img = document.createElement("img"); | |
img.src = dataURL; | |
img.classList.add('preview-image'); | |
document.body.appendChild(img); | |
} | |
function doneGeneratingPreviews() { | |
for (key in state.renderedFontPreviews) { | |
addImage(state.renderedFontPreviews[key]); | |
} | |
} | |
// ***** render component stuff | |
var demoFontsToRender = ["Komika Hand Bold Italic", "Komika Hand Bold", "Komika Hand Italic", "Komika Hand Regular", "Komika Parch Regular", "Komika Title - Axis Regular", "Komika Title - Kaps Regular", "Komika Title - Paint Regular", "Komika Title - Wide Regular", "Komika Title Regular"]; | |
var color = '#ff00ff'; | |
demoFontsToRender.forEach(function (name) { | |
addFontPreviewToRender(name, color, name); | |
}); | |
// get initial list of job params | |
var initialJobParams = getFontPreviewsToRender(); | |
var i = 0; | |
var windowMethod = 'requestAnimationFrame'; // requestAnimationFrame | |
function processRenderJobs(jobParams) { | |
if (i > 100) { | |
console.log('emergency brake'); | |
return; | |
} | |
if (renderMap1Busy) { | |
// requeue | |
window[windowMethod](function () { | |
return processRenderJobs(jobParams); | |
}); | |
return; | |
} | |
// else | |
renderMap1Busy = true; | |
var _jobParams = _toArray(jobParams); | |
var params = _jobParams[0]; | |
var remainingParams = _jobParams.slice(1); | |
var style = generateFontPreviewStyle({ | |
owner: owner, name: params.name, text: params.text, color: params.color | |
}); | |
renderMap1.setStyle(style); | |
renderMap1.once('idle', function () { | |
var dataURL = renderMap1.getCanvas().toDataURL(); | |
// Save the rendered image data into state | |
saveRenderedFontPreviewData(params.name, params.text, params.color, dataURL); | |
renderMap1Busy = false; | |
// we have more to process, so queue up the remaining | |
if (remainingParams.length) { | |
window[windowMethod](function () { | |
return processRenderJobs(remainingParams); | |
}); | |
} else { | |
doneGeneratingPreviews(); | |
} | |
}); | |
} | |
window[windowMethod](function () { | |
return processRenderJobs(initialJobParams); | |
}); | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript">mapboxgl.accessToken = "pk.eyJ1IjoianNlcHBpbWJ4IiwiYSI6ImNqbGU1ODdtMzBpZjUzcG1pMWJnaHB2aHgifQ.xGVwKUpyJ-S5iyaLq7GFLA"; | |
const owner = 'jseppimbx'; | |
const renderMap1 = new mapboxgl.Map({ | |
container: 'render_map_1', | |
center: [0.525, 0], // TODO: real math? | |
zoom: 9, | |
pitch: 0, | |
bearing: 0, | |
fadeDuration: 0, | |
preserveDrawingBuffer: true, | |
localIdeographFontFamily: false | |
}); | |
let renderMap1Busy = false; | |
function generateFontPreviewStyle({ owner, name, text, color }) { | |
return { | |
version: 8, | |
glyphs: `mapbox://fonts/${owner}/{fontstack}/{range}.pbf`, | |
sources: { | |
font: { | |
type: "geojson", | |
data: { | |
type: "FeatureCollection", | |
features: [ | |
{ | |
type: "Feature", | |
geometry: { | |
type: "Point", | |
coordinates: [0, 0] | |
}, | |
properties: {} | |
} | |
] | |
} | |
} | |
}, | |
layers: [ | |
{ | |
id: "preview", | |
source: "font", | |
type: "symbol", | |
layout: { | |
"text-justify": "left", | |
"text-anchor": "left", | |
"text-field": text, | |
"text-font": [name], | |
"text-max-width": 200, | |
"text-size": 80, | |
"text-line-height": 5 // TODO: does this do anything? | |
}, | |
paint: { | |
"text-color": color, | |
"text-halo-color": color, | |
"text-halo-width": 0.3, | |
"text-halo-blur": 0 | |
} | |
}, | |
{ | |
// Show a circle here for easier | |
// positioning debugging | |
id: "markers", | |
source: "font", | |
type: "circle", | |
layout: {}, | |
paint: { | |
"circle-radius": 5, | |
"circle-color": "yellow", | |
"circle-stroke-width": 0 | |
} | |
} | |
] | |
}; | |
} | |
// ***** redux-like stuff | |
const state = {}; | |
state.fontPreviewsToRender = new Set(); | |
state.renderedFontPreviews = {}; | |
function makeFontKey(name, color, text) { | |
return `${name}^${color}^${text}`; | |
} | |
function fontKeyToParams(key) { | |
const parts = key.split('^'); | |
return { | |
name: parts[0], | |
color: parts[1], | |
text: parts[2] | |
}; | |
} | |
function addFontPreviewToRender(name, color, text) { | |
const key = makeFontKey(name, color, text); | |
if (!state.fontPreviewsToRender.has(key)) { | |
state.fontPreviewsToRender.add(key); | |
} | |
} | |
function getFontPreviewsToRender() { | |
const paramsToRender = []; | |
state.fontPreviewsToRender.forEach((key) => { | |
const params = fontKeyToParams(key); | |
paramsToRender.push(params); | |
}); | |
return paramsToRender; | |
} | |
function saveRenderedFontPreviewData(name, color, text, dataURL) { | |
const key = makeFontKey(name, color, text); | |
state.renderedFontPreviews[key] = dataURL; | |
if (state.fontPreviewsToRender.has(key)) { | |
state.fontPreviewsToRender.delete(key); | |
} | |
} | |
// ***** fake output helpers | |
function addImage(dataURL) { | |
const img = document.createElement("img"); | |
img.src = dataURL; | |
img.classList.add('preview-image'); | |
document.body.appendChild(img); | |
} | |
function doneGeneratingPreviews() { | |
for (key in state.renderedFontPreviews) { | |
addImage(state.renderedFontPreviews[key]) | |
} | |
} | |
// ***** render component stuff | |
const demoFontsToRender = [ | |
"Komika Hand Bold Italic", | |
"Komika Hand Bold", | |
"Komika Hand Italic", | |
"Komika Hand Regular", | |
"Komika Parch Regular", | |
"Komika Title - Axis Regular", | |
"Komika Title - Kaps Regular", | |
"Komika Title - Paint Regular", | |
"Komika Title - Wide Regular", | |
"Komika Title Regular" | |
]; | |
const color = '#ff00ff'; | |
demoFontsToRender.forEach((name) => { | |
addFontPreviewToRender(name, color, name); | |
}); | |
// get initial list of job params | |
const initialJobParams = getFontPreviewsToRender(); | |
let i = 0; | |
const windowMethod = 'requestAnimationFrame'; // requestAnimationFrame | |
function processRenderJobs(jobParams) { | |
if (i > 100) { | |
console.log('emergency brake'); | |
return; | |
} | |
if (renderMap1Busy) { | |
// requeue | |
window[windowMethod](() => processRenderJobs(jobParams)); | |
return; | |
} | |
// else | |
renderMap1Busy = true; | |
const [params, ...remainingParams] = jobParams; | |
const style = generateFontPreviewStyle({ | |
owner, name: params.name, text: params.text, color: params.color | |
}); | |
renderMap1.setStyle(style); | |
renderMap1.once('idle', () => { | |
const dataURL = renderMap1.getCanvas().toDataURL(); | |
// Save the rendered image data into state | |
saveRenderedFontPreviewData(params.name, params.text, params.color, dataURL) | |
renderMap1Busy = false; | |
// we have more to process, so queue up the remaining | |
if (remainingParams.length) { | |
window[windowMethod](() => processRenderJobs(remainingParams)); | |
} else { | |
doneGeneratingPreviews(); | |
} | |
}); | |
} | |
window[windowMethod](() => processRenderJobs(initialJobParams)); | |
</script></body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); } | |
mapboxgl.accessToken = "pk.eyJ1IjoianNlcHBpbWJ4IiwiYSI6ImNqbGU1ODdtMzBpZjUzcG1pMWJnaHB2aHgifQ.xGVwKUpyJ-S5iyaLq7GFLA"; | |
var owner = 'jseppimbx'; | |
var renderMap1 = new mapboxgl.Map({ | |
container: 'render_map_1', | |
center: [0.525, 0], // TODO: real math? | |
zoom: 9, | |
pitch: 0, | |
bearing: 0, | |
fadeDuration: 0, | |
preserveDrawingBuffer: true, | |
localIdeographFontFamily: false | |
}); | |
var renderMap1Busy = false; | |
function generateFontPreviewStyle(_ref) { | |
var owner = _ref.owner; | |
var name = _ref.name; | |
var text = _ref.text; | |
var color = _ref.color; | |
return { | |
version: 8, | |
glyphs: 'mapbox://fonts/' + owner + '/{fontstack}/{range}.pbf', | |
sources: { | |
font: { | |
type: "geojson", | |
data: { | |
type: "FeatureCollection", | |
features: [{ | |
type: "Feature", | |
geometry: { | |
type: "Point", | |
coordinates: [0, 0] | |
}, | |
properties: {} | |
}] | |
} | |
} | |
}, | |
layers: [{ | |
id: "preview", | |
source: "font", | |
type: "symbol", | |
layout: { | |
"text-justify": "left", | |
"text-anchor": "left", | |
"text-field": text, | |
"text-font": [name], | |
"text-max-width": 200, | |
"text-size": 80, | |
"text-line-height": 5 // TODO: does this do anything? | |
}, | |
paint: { | |
"text-color": color, | |
"text-halo-color": color, | |
"text-halo-width": 0.3, | |
"text-halo-blur": 0 | |
} | |
}, { | |
// Show a circle here for easier | |
// positioning debugging | |
id: "markers", | |
source: "font", | |
type: "circle", | |
layout: {}, | |
paint: { | |
"circle-radius": 5, | |
"circle-color": "yellow", | |
"circle-stroke-width": 0 | |
} | |
}] | |
}; | |
} | |
// ***** redux-like stuff | |
var state = {}; | |
state.fontPreviewsToRender = new Set(); | |
state.renderedFontPreviews = {}; | |
function makeFontKey(name, color, text) { | |
return name + '^' + color + '^' + text; | |
} | |
function fontKeyToParams(key) { | |
var parts = key.split('^'); | |
return { | |
name: parts[0], | |
color: parts[1], | |
text: parts[2] | |
}; | |
} | |
function addFontPreviewToRender(name, color, text) { | |
var key = makeFontKey(name, color, text); | |
if (!state.fontPreviewsToRender.has(key)) { | |
state.fontPreviewsToRender.add(key); | |
} | |
} | |
function getFontPreviewsToRender() { | |
var paramsToRender = []; | |
state.fontPreviewsToRender.forEach(function (key) { | |
var params = fontKeyToParams(key); | |
paramsToRender.push(params); | |
}); | |
return paramsToRender; | |
} | |
function saveRenderedFontPreviewData(name, color, text, dataURL) { | |
var key = makeFontKey(name, color, text); | |
state.renderedFontPreviews[key] = dataURL; | |
if (state.fontPreviewsToRender.has(key)) { | |
state.fontPreviewsToRender['delete'](key); | |
} | |
} | |
// ***** fake output helpers | |
function addImage(dataURL) { | |
var img = document.createElement("img"); | |
img.src = dataURL; | |
img.classList.add('preview-image'); | |
document.body.appendChild(img); | |
} | |
function doneGeneratingPreviews() { | |
for (key in state.renderedFontPreviews) { | |
addImage(state.renderedFontPreviews[key]); | |
} | |
} | |
// ***** render component stuff | |
var demoFontsToRender = ["Komika Hand Bold Italic", "Komika Hand Bold", "Komika Hand Italic", "Komika Hand Regular", "Komika Parch Regular", "Komika Title - Axis Regular", "Komika Title - Kaps Regular", "Komika Title - Paint Regular", "Komika Title - Wide Regular", "Komika Title Regular"]; | |
var color = '#ff00ff'; | |
demoFontsToRender.forEach(function (name) { | |
addFontPreviewToRender(name, color, name); | |
}); | |
// get initial list of job params | |
var initialJobParams = getFontPreviewsToRender(); | |
var i = 0; | |
var windowMethod = 'requestAnimationFrame'; // requestAnimationFrame | |
function processRenderJobs(jobParams) { | |
if (i > 100) { | |
console.log('emergency brake'); | |
return; | |
} | |
if (renderMap1Busy) { | |
// requeue | |
window[windowMethod](function () { | |
return processRenderJobs(jobParams); | |
}); | |
return; | |
} | |
// else | |
renderMap1Busy = true; | |
var _jobParams = _toArray(jobParams); | |
var params = _jobParams[0]; | |
var remainingParams = _jobParams.slice(1); | |
var style = generateFontPreviewStyle({ | |
owner: owner, name: params.name, text: params.text, color: params.color | |
}); | |
renderMap1.setStyle(style); | |
renderMap1.once('idle', function () { | |
var dataURL = renderMap1.getCanvas().toDataURL(); | |
// Save the rendered image data into state | |
saveRenderedFontPreviewData(params.name, params.text, params.color, dataURL); | |
renderMap1Busy = false; | |
// we have more to process, so queue up the remaining | |
if (remainingParams.length) { | |
window[windowMethod](function () { | |
return processRenderJobs(remainingParams); | |
}); | |
} else { | |
doneGeneratingPreviews(); | |
} | |
}); | |
} | |
window[windowMethod](function () { | |
return processRenderJobs(initialJobParams); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment