Last active
June 26, 2025 14:31
-
-
Save FelipeBudinich/a9d9b1eab8a938785964e4641c8459e4 to your computer and use it in GitHub Desktop.
`ig.WebFont` allows you to dynamically load and render TrueType or OpenType fonts using the browser's FontFace API, fully integrated with ImpactJS's resource loader system. docs: https://a.fun.voyage/Code/ig.WebFont
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
| ig.module( | |
| 'plugins.webfont' | |
| ) | |
| .defines(function(){ "use strict"; | |
| ig.WebFont = ig.Class.extend({ | |
| // ------------------------------------------------------------------------- | |
| // Properties required by ig.Loader | |
| // ------------------------------------------------------------------------- | |
| path : '', | |
| loaded : false, | |
| failed : false, | |
| loadCallback : null, | |
| // ------------------------------------------------------------------------- | |
| // Draw‑time attributes (similar to ig.Font) | |
| // ------------------------------------------------------------------------- | |
| cssFont : '20px sans-serif', | |
| size : 20, | |
| family : '', | |
| weight : 'normal', | |
| letterSpacing: 0, | |
| lineSpacing : 0, | |
| alpha : 1, | |
| color : '#ffffff', | |
| outline : null, | |
| outlineWidth : 2, | |
| staticInstantiate: function(){ return null; }, | |
| init: function( desc ){ | |
| if( typeof desc === 'string' ){ | |
| desc = { file: desc }; | |
| } | |
| this.path = desc.file; | |
| this.family = desc.family; | |
| // Optional overrides | |
| if( 'size' in desc ) this.size = desc.size; | |
| if( 'weight' in desc ) this.weight = desc.weight; | |
| // Build the CSS font string: [weight] [size]px [family] | |
| this.cssFont = desc.css || ( | |
| (this.weight ? this.weight + ' ' : '') + | |
| this.size + 'px ' + | |
| this.family | |
| ); | |
| if( 'color' in desc ) this.color = desc.color; | |
| if( 'alpha' in desc ) this.alpha = desc.alpha; | |
| if( 'letterSpacing' in desc ) this.letterSpacing = desc.letterSpacing; | |
| if( 'lineSpacing' in desc ) this.lineSpacing = desc.lineSpacing; | |
| if( 'outline' in desc ) this.outline = desc.outline; | |
| if( 'outlineWidth' in desc ) this.outlineWidth = desc.outlineWidth; | |
| this.load(); | |
| }, | |
| load: function( cb ){ | |
| if( this.loaded ){ | |
| cb && cb( this.path, true ); | |
| return; | |
| } | |
| if( this.failed ){ | |
| cb && cb( this.path, false ); | |
| return; | |
| } | |
| // Not ready? Let ig.Loader queue us. | |
| if( !ig.ready ){ | |
| ig.addResource( this ); | |
| return; | |
| } | |
| this.loadCallback = cb || null; | |
| // 1. Font already finished loading elsewhere? | |
| var state = ig.WebFont.faceCache[this.path]; | |
| if( state === 'ok' ){ | |
| this.loaded = true; | |
| this.loadCallback && this.loadCallback( this.path, true ); | |
| return; | |
| } | |
| if( state === 'fail' ){ | |
| this.failed = true; | |
| this.loadCallback && this.loadCallback( this.path, false ); | |
| return; | |
| } | |
| // 2. Someone *is* loading it – hook into their promise. | |
| if( state && typeof state.then === 'function' ){ | |
| state.then( | |
| function(){ this.loaded = true; this.loadCallback && this.loadCallback( this.path, true ); }.bind(this), | |
| function(){ this.failed = true; this.loadCallback && this.loadCallback( this.path, false ); }.bind(this) | |
| ); | |
| return; | |
| } | |
| // 3. First one here – start the load and stash the promise. | |
| var url = ig.prefix + this.path + ig.nocache; | |
| // Build descriptors for FontFace (weight/style) | |
| var descriptors = {}; | |
| var w = this.weight.toString(); | |
| // Check for italic | |
| if( w.indexOf('italic') !== -1 ){ | |
| descriptors.style = 'italic'; | |
| } | |
| // Extract numeric weight if present | |
| var num = w.match(/\d+/); | |
| if( num ){ | |
| descriptors.weight = num[0]; | |
| } else if( w === 'bold' ){ descriptors.weight = '700'; } | |
| var face = new FontFace( this.family, 'url(' + url + ')', descriptors ); | |
| var promise = face.load().then(function( f ){ | |
| document.fonts.add( f ); | |
| ig.WebFont.faceCache[this.path] = 'ok'; | |
| this.loaded = true; | |
| this.loadCallback && this.loadCallback( this.path, true ); | |
| }.bind(this)).catch(function( err ){ | |
| console.warn( 'WebFont load failed:', err ); | |
| ig.WebFont.faceCache[this.path] = 'fail'; | |
| this.failed = true; | |
| this.loadCallback && this.loadCallback( this.path, false ); | |
| }.bind(this)); | |
| ig.WebFont.faceCache[this.path] = promise; | |
| }, | |
| widthForString: function( text ){ | |
| var ctx = ig.system.context; | |
| ctx.save(); | |
| ctx.font = this.cssFont; | |
| var width = 0; | |
| var lines = text.split('\n'); | |
| for( var i = 0; i < lines.length; i++ ){ | |
| var w = ctx.measureText( lines[i] ).width + | |
| this.letterSpacing * ( lines[i].length - 1 ); | |
| if( w > width ){ width = w; } | |
| } | |
| ctx.restore(); | |
| return width; | |
| }, | |
| heightForString: function( text ){ | |
| var size = this.size; | |
| var lines = text.split('\n').length; | |
| return lines * ( size + this.lineSpacing ); | |
| }, | |
| draw: function( text, x, y, align ){ | |
| if( !this.loaded ){ return; } | |
| var ctx = ig.system.context; | |
| ctx.save(); | |
| ctx.font = this.cssFont; | |
| ctx.fillStyle = this.color; | |
| ctx.textBaseline = 'middle'; | |
| if( this.alpha !== 1 ){ | |
| ctx.globalAlpha = this.alpha; | |
| } | |
| switch( align ){ | |
| case ig.WebFont.ALIGN.RIGHT : ctx.textAlign = 'right'; break; | |
| case ig.WebFont.ALIGN.CENTER: ctx.textAlign = 'center'; break; | |
| default : ctx.textAlign = 'left'; | |
| } | |
| var lineHeight = this.size + this.lineSpacing; | |
| var lines = text.split('\n'); | |
| for( var i = 0; i < lines.length; i++ ){ | |
| var lineY = y + i * lineHeight; | |
| if( this.outline ){ | |
| ctx.strokeStyle = this.outline; | |
| ctx.lineWidth = this.outlineWidth; | |
| ctx.strokeText( lines[i], x, lineY ); | |
| } | |
| ctx.fillText( lines[i], x, lineY ); | |
| } | |
| ctx.restore(); | |
| } | |
| }); | |
| // Alignment enum | |
| ig.WebFont.ALIGN = { | |
| LEFT : 0, | |
| RIGHT : 1, | |
| CENTER: 2 | |
| }; | |
| ig.WebFont.faceCache = {}; // path → 'ok' | 'fail' | Promise | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment