Change font size of Canvas without knowing font family - javascript

Is there a way to only change the font size of a canvas context without having to know/write the font family.
var ctx = document.getElementById("canvas").getContext("2d");
ctx.font = '20px Arial'; //Need to speficy both size and family...
Note:
ctx.fontSize = '12px'; //doesn't exist so won't work...
ctx.style.fontSize = '20 px' //doesn't exist so won't work...
//we are changing the ctx, not the canvas itself
Other note: I could do something like: detect where 'px' is, remove what's before 'px' and replace it by my font size. But I'd like something easier than that if possible.

Here is an easier and cleaner way of changing the font size that will work regardless if you are using font-variant or font-weight or not.
First some RegEx to match and return the current font size
const match = /(?<value>\d+\.?\d*)/;
Set an absolute size, assuming your new font size is 12px
ctx.font = ctx.font.replace(match, 12);
Set a relative size, increase by 5:
const newSize = parseFloat(ctx.font.match(match).groups.value + 5);
ctx.font = ctx.font.replace(match, newSize);
Working example:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// Match on digit at least once, optional decimal, and optional digits
// Named group `value`
const match = /(?<value>\d+\.?\d*)/;
const setFontSize = (size) => ctx.font = ctx.font.replace(match, size);
const adjustFontSize = (amount) => {
const newSize = parseFloat(ctx.font.match(match).groups.value + amount);
return ctx.font = ctx.font.replace(match, newSize);
}
// Default font is 12px sans-serif
console.log( setFontSize(18) ); // 18px sans-serif
console.log( adjustFontSize(2) ); // 20px sans-serif
console.log( adjustFontSize(-5) ); // 15px sans-serif
<canvas id="canvas"></canvas>

Update: (from comments) There is no way around specifying font. The Canvas' font is modeled after the short-hand version of font in CSS.
However, there is always a font set on the canvas (or a font type) so what you can do is to first extract the current font by using it like this:
var cFont = ctx.font;
Then replace size arguments and set it back (note that there might be a style parameter there as well).
A simple split for the sake of example:
var fontArgs = ctx.font.split(' ');
var newSize = '12px';
ctx.font = newSize + ' ' + fontArgs[fontArgs.length - 1]; /// using the last part
You will need support for style if needed (IIRC it comes first if used).
Notice that font-size is fourth parameter, so this will not work if you will have/not have font-variant(bold,italic,oblique), font-variant(normal, small-caps) and font-weight(bold etc).

A cleaner way to not worry about scraping every other parameter:
canvas.style.font = canvas.getContext("2d").font;
canvas.style.fontSize = newVal + "px";
canvas.getContext("2d").font = canvas.style.font;
Explanation:
As mentioned in the first answer, the canvas context will always have a font. So canvas.getContext("2d").font might return something like "10px sans-serif". To not scrape this string we can use the Canvas DOM element (or any other element) to parse the full font spec and populate the other font fields. Which means that after setting:
canvas.style.font = "10px sans-serif";
canvas.style.fontSize = "26px";
the canvas.style.font will become "26px sans-serif". We can then pass this font spec back to the canvas context.

To correctly answer your question, there is no way to change the font size of a canvas context without having to know/write the font family.

try this (using jquery):
var span = $('<span/>').css('font', context.font).css('visibility', 'hidden');
$('body').append(span);
span[0].style.fontWeight = 'bold';
span[0].style.fontSize = '12px';
//add more style here.
var font = span[0].style.font;
span.remove();
return font;

Related

Draw Font Awesome icons to canvas based on class names

I want to be able to:
Get the Font Awesome 5 unicode character for any given Font Awesome class name (fas fa-check-circle, etc...)
Draw that unicode character to an html5 canvas
How would I go about doing this?
To get the correct unicode character and other important data for a Font Awesome 5 icon class and draw it onto an html5 canvas:
// create an icon with the Font Awesome class name you want
const i = document.createElement('i');
i.setAttribute('class', 'fas fa-check-circle');
document.body.appendChild(i);
// get the styles for the icon you just made
const iStyles = window.getComputedStyle(i);
const iBeforeStyles = window.getComputedStyle(i, ':before');
const fontFamily = iStyles.getPropertyValue('font-family');
const fontWeight = iStyles.getPropertyValue('font-weight');
const fontSize = '40px'; // just to make things a little bigger...
const canvasFont = `${fontWeight} ${fontSize} ${fontFamily}`; // should be something like: '900 40px "Font Awesome 5 Pro"'
const icon = String.fromCodePoint(iBeforeStyles.getPropertyValue('content').codePointAt(1)); // codePointAt(1) because the first character is a double quote
const ctx = myCanvas.getContext('2d');
ctx.font = canvasFont;
ctx.fillStyle = 'red';
ctx.textAlign = 'center';
ctx.fillText(icon, myCanvas.width / 2, myCanvas.height / 2);
When you do this, make sure the Font Awesome 5 font has actually loaded when you do the drawing. I made the mistake of only drawing once when I was testing and it resulted in only a box showing up. When the icon has loaded it should appear like this:

Stop pixel font from being blurred when rendered

I am trying to create a little game in a pixel retro look and I am using a pixelated font. Unfortunately I am unable to render the font with edges as sharp as they are in the source font.
About my mcve:
I am using this font for fonts/pokemon_classic.ttf (I found no way to host that font online, so no jsfiddle), but you can use any pixel font you like.
The example below renders the font like this:
How can I make this text render as sharp as it is in the source font? It should look like this (edited image):
the scale of root may change during runtime to fit the screen
A less elegant solution which would probably work is to fix the alpha of each pixel to be either 0 or 1 depending on some threshold, but I don't know how to do this.
JS:
PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
let scale = 30;
let app = new PIXI.Application();
document.body.appendChild(app.view);
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
let root = new PIXI.Container();
app.stage.addChild(root);
root.scale.set(scale);
document.fonts.load('8pt "pokemon"').then(() => {
let text = new PIXI.Text("Test", {fontFamily: 'pokemon', fontSize: 8, fill: 0xff1010});
root.addChild(text);
});
CSS:
#font-face {
font-family: 'pokemon';
src: url("fonts/pokemon_classic.ttf");
}
* {
padding: 0;
margin: 0;
}
Things that can make text blurry....
SCALE_MODE
Sub-pixel positioning. Turn on roundPixels when creating new PIXI.Application for v4, v5 you can set globally via PIXI.settings.ROUND_PIXELS = true;, or indivudally, displayObject.roundPixels = true;
Scaling
So you're good on #1, but #2 and #3 could be issues.
I resolved it by setting resolution like this:
text.resolution = window.devicePixel * scale
It will re-render the text leading to bad performance, so I just apply it for the last value of scale.
If you don't mind having a double resolution for the whole app...
My Pixi.js text and textures were blurry.
I increased the resolution of the whole Pixi app.
Then your canvas size will double.
You can then you options.autoDensity to fit double resolution in the same canvas. https://pixijs.download/dev/docs/PIXI.Application.html
// Pixi.js API from v6.5.1
import * as PIXI from 'pixi.js';
PIXI.settings.PRECISION_FRAGMENT = PIXI.PRECISION.HIGH; // might help a bit
PIXI.settings.ROUND_PIXELS = true; // might help a bit
PIXI.settings.RESOLUTION = 2;
const app = new PIXI.Application({ width: 750, height: 400, autoDensity: true });

How do I set width and positioning of an object with javascript from a variable?

I am trying to create a button in javascript and make that button positioned below a canvas, the same width as the canvas, however, I don't seem to be able to set the top and width as a variable (the code is ignored). Here is my code: (problem lines commented)
var myButton = document.createElement('button');
myButton.style.position = 'absolute';
myButton.style.left = '0px';
myButton.style.top = canvasHeight; //
myButton.style.width = canvasWidth; //
myButton.style.height = '100px';
myButton.innerHTML = 'Restart!';
document.body.appendChild(myButton);
Note that the canvas is resizable, so I can't just type in a px value.
Here's my comment in an answer:
Make sure you have the correct format in canvasHeight and canvasWidth, e.g. "100px" rather than just "100".

Completely match HTML5 Canvas font to Snapchat's font

I'm trying to recreate a Snapchat "snap" with HTML5 Canvas. The part that has me stumped is getting the font to look the same. The font Snapchat uses seems to be Helvetica, but I can't get my text to look the same as that of a real snap.
Here's my attempt of matching the font. I've drawn my text on top of an actual screenshot of a snap so it's easy to compare. You can edit the JavaScript variables to try to match the text (JSFiddle: http://jsfiddle.net/9RB88/1/).
HTML:
<img src='http://i.imgur.com/tivQ8xJ.jpg'>
<canvas width='640' height='1136'></canvas>
CSS:
img {display:none}
JavaScript:
//--- EDIT THESE ----------------
// text styles...
var fontFamily = 'Helvetica';
var fontSize = '35px';
var fontWeight = 'normal';
// http://css-tricks.com/almanac/properties/f/font-weight/
var onTop = false;
// set this to "true" to place our text on top of the real text (for more precise comparing)
var differentColours = false;
// draw our text/background different colours, making it easier to compare (use when "onTop" is "true")
// tweak where the text is drawn
var horOffset = 2;
var vertOffset = 0;
//-------------------------------
var can = document.getElementsByTagName('canvas')[0];
var c = can.getContext('2d');
c.fillStyle = 'white';
c.fillRect(0, 0, can.width, can.height);
var img = document.getElementsByTagName('img')[0];
c.drawImage(img, 0, 0);
var top;
if(onTop) top = 531;
else top = 450;
if(differentColours) c.fillStyle = 'orange';
else c.fillStyle = 'black';
c.globalAlpha = 0.6;
c.fillRect(0, top, can.width, 74);
c.globalAlpha = 1;
var text = 'Template example thingy $&#?! 123';
if(differentColours) c.fillStyle = 'red';
else c.fillStyle = 'white';
c.textAlign = 'center';
c.font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
c.fillText(text, can.width/2 + horOffset, top + 50 + vertOffset);
JSFiddle: http://jsfiddle.net/9RB88/1/
It seems like one of the issues is letter spacing, but that's not possible to change in HTML5 Canvas.
What can I do?
Snapchat may embed a very specific variant or similar font to Helvetica. That doesn't mean the user loading the page (incl. on your own machine) will have that font installed on the system.
If the user doesn't have the font installed the browser will look for similar fonts, for example, it will use Arial on Windows and if that is not available use another sans-serif font. On Mac and Linux the same thing. This mean it will perhaps closely match but will not be the same font. If they don't use a web font the snap could have been taken on a different OS then you are using resulting in a different font being rendered.
Or in other words - when you specify "Helvetica" as font family the system will try to find a font in that family but it isn't given that Helvetica is the font you'll get. The browser will try to find the closest matching font available on the system in use.
You have to embed the very same font they are using as a web font otherwise there is no guarantee that the font will be the same.
To find out which font they are using (if any) open the stylesheets they're using.
My 2 cents..

Sizing text in a canvas

The canvas seems to default to a width of 300 and a height of 150. What I want it a much smaller canvas with text on it, a tooltip. As such I have three queries about this sample (on JSfiddle):
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
c.style.height="25px";
//2: This stretches the visible portion of the text. Not what I wanted.
//c.style.width="125px";
ctx.font="130px Georgia";
//3: If I don't want to set the font family this doesn't seem to set the font size.
//ctx.fontSize="130";
ctx.fillText("Hello World!",0,130);
</script>
</body>
</html>
I have reduced the size of the canvas but I need to increase the font size dramatically which seems an odd thing to have to do. Is this approach correct?
I can't get the whole 'Hello World' to display as increasing the size of the canvas stretches the visible portion of the text. How do I show the whole text?
How can I set the font size without setting the font family?
Thanks.
Use this instead:
c.width = 200; // just for example. Defined in pixels using integer values
c.height = 25;
CSS only affects the element but not the bitmap used for the context. Think of canvas as an edit-able image. An image would simply be stretched with CSS while the actual width and height would be the same as the original.
You can't set the font size without specifying font family.
You can extract current font and alter that:
var cFont = ctx.font,
parts = cFont.split(' ');
if (parts.length === 2) {
ctx.font = newSize + 'px ' + parts[1];
}
else if (parts.length === 3) {
ctx.font = parts[0] + ' ' + newSize + 'px ' + parts[2]; //bold/italic/.. used
}

Categories