Reputation: 18491
I currently have this http://jsfiddle.net/dgAEY/ which works perfectly, I just need to figure out a way to size the font when it gets too long. I've looked into Auto-size dynamic text to fill fixed size container and I've tried to apply the Jquery function they posted but I couldn't get it to work.
HTML
<form action="" method="POST" id="nametag" class="nametag">
Line1:
<input type="text" id="line1" name="line1" style="width:250px;" /><br>
Line2:
<input type="text" id="line2" name="line2" style="width:250px;" /><br>
Line3:
<input type="text" id="line3" name="line3" style="width:250px;" /><br>
Line4:
<input type="text" id="line4" name="line4" style="width:250px;" /><br>
<br><br><b>Name Tag</b><br>
<canvas width="282px" height="177px" id="myCanvas" style="border: black thin solid;"></canvas>
</form>
JavaScript
$(document).ready(function () {
var canvas = $('#myCanvas')[0];
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 0, 0);
};
imageObj.src = "http://dummyimage.com/282x177/FFF/FFF";
$('#nametag').bind('change keyup input', updateCanvas);
$('#line2').bind('click', updateCanvas);
$('#line3').bind('click', updateCanvas);
$('#line4').bind('click', updateCanvas);
function updateCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(imageObj, 0, 0);
context.textAlign = "center";
context.font = "bold 18pt Arial";
context.fillText($('#line1').val(), canvas.width * 0.5, 70);
context.font = "12pt Arial";
context.fillText($('#line2').val(), canvas.width * 0.5, 90);
context.fillText($('#line3').val(), canvas.width * 0.5, 120);
context.fillText($('#line4').val(), canvas.width * 0.5, 140);
}
});
Upvotes: 18
Views: 28738
Reputation: 105015
You can use context.measureText to get the pixel width of any given text in the current font.
Then if that width is too big, reduce the font size until it fits.
context.font="14px verdana";
var width = context.measureText("Hello...Do I fit on the canvas?").width
if(width>myDesiredWidth) // then reduce the font size and re-measure
Demo:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
fitTextOnCanvas("Hello, World!", "verdana", 125);
function fitTextOnCanvas(text, fontface, yPosition) {
// start with a large font size
var fontsize = 300;
// lower the font size until the text fits the canvas
do {
fontsize--;
context.font = fontsize + "px " + fontface;
} while (context.measureText(text).width > canvas.width)
// draw the text
context.fillText(text, 0, yPosition);
alert("A fontsize of " + fontsize + "px fits this text on the canvas");
}
body {
background-color: ivory;
}
#canvas {
border: 1px solid red;
}
<canvas id="canvas" width=300 height=300></canvas>
Upvotes: 35
Reputation: 1861
If you wanna do it the other way around (size the canvas to the font, not the font to the canvas), you can do something like the following (not fully tested):
var canvas = document.getElementById('canvas00')
var ctx = canvas.getContext('2d')
var str = 'Jesus is God Almighty'
ctx.font = 'bold 3em'
var metrics = ctx.measureText(str)
canvas.width = metrics.width
canvas.height = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent // Use *fontBoundingBox* or *actualBoundingBox*, depending on your needs
ctx.font = 'bold 3em' // For some reason you have to call this again?
ctx.fillStyle = '#3240ff'
ctx.fillText(str, 0, canvas.height - metrics.fontBoundingBoxDescent)
Upvotes: 0
Reputation: 60913
Base on @Veetaha answer, here is my function to fit multiple lines of center text
function getFontSizeToFit(ctx, text, fontFace, width, height) {
ctx.font = `1px ${fontFace}`;
let fitFontWidth = Number.MAX_VALUE
const lines = text.match(/[^\r\n]+/g);
lines.forEach(line => {
fitFontWidth = Math.min(fitFontWidth, width / ctx.measureText(line).width)
})
let fitFontHeight = height / (lines.length * 1.2); // if you want more spacing between line, you can increase this value
return Math.min(fitFontHeight, fitFontWidth)
}
Demo
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
let text = "Hello World \n Hello World 2222 \n AAAAA \n BBBB"
fitTextCenter()
function fitTextCenter() {
let fontSize = getFontSizeToFit(ctx, text, "Arial", c.width, c.height)
ctx.font = fontSize + "px Arial"
fillTextCenter(ctx, text, 0, 0, c.width, c.height)
}
function fillTextCenter(ctx, text, x, y, width, height) {
ctx.textBaseline = 'middle';
ctx.textAlign = "center";
const lines = text.match(/[^\r\n]+/g);
for (let i = 0; i < lines.length; i++) {
let xL = (width - x) / 2
let yL = y + (height / (lines.length + 1)) * (i + 1)
ctx.fillText(lines[i], xL, yL)
}
}
function getFontSizeToFit(ctx, text, fontFace, width, height) {
ctx.font = `1px ${fontFace}`;
let fitFontWidth = Number.MAX_VALUE
const lines = text.match(/[^\r\n]+/g);
lines.forEach(line => {
fitFontWidth = Math.min(fitFontWidth, width / ctx.measureText(line).width)
})
let fitFontHeight = height / (lines.length * 1.2); // if you want more spacing between line, you can increase this value
return Math.min(fitFontHeight, fitFontWidth)
}
function testScaleUpX() {
c.width += 1
fitTextCenter()
}
function testScaleUpY() {
c.height += 1
fitTextCenter()
}
function testScaleDownX() {
c.width -= 1
fitTextCenter()
}
function testScaleDownY() {
c.height -= 1
fitTextCenter()
}
<canvas id="myCanvas" width="200" height="80" style="border:1px solid #000;"></canvas>
<button onclick="testScaleUpX()">+ X</button>
<button onclick="testScaleUpY()">+ Y</button>
<button onclick="testScaleDownX()">- X</button>
<button onclick="testScaleDownY()">- Y</button>
Upvotes: 4
Reputation: 862
Simple and efficient solution for DOM environment, no loops, just do one sample measurement and scale the result appropriately.
function getFontSizeToFit(text: string, fontFace: string, maxWidth: number) {
const ctx = document.createElement('canvas').getContext('2d');
ctx.font = `1px ${fontFace}`;
return maxWidth / ctx.measureText(text).width;
}
Beware that if you use npm 'canvas' module for NodeJs environment, the result won't be that accurate as they use some custom C++ implementation that returns only integer sample width.
Upvotes: 13
Reputation: 127
I have created an improved version of markE's code. It becomes apparent that his code is slower if you have multiple texts. The browsers are good at caching but on the first run you can definitely get a noticeable lag even with just a handful of lines that need scaling.
Try these two versions:
Original method (markE):
http://jsfiddle.net/be6ppdre/29/
Faster method:
http://jsfiddle.net/ho9thkvo/2/
The main code is here:
function fitTextOnCanvas(text, fontface){
var size = measureTextBinaryMethod(text, fontface, 0, 600, canvas.width);
return size;
}
function measureTextBinaryMethod(text, fontface, min, max, desiredWidth) {
if (max-min < 1) {
return min;
}
var test = min+((max-min)/2); //Find half interval
context.font=test+"px "+fontface;
measureTest = context.measureText(text).width;
if ( measureTest > desiredWidth) {
var found = measureTextBinaryMethod(text, fontface, min, test, desiredWidth)
} else {
var found = measureTextBinaryMethod(text, fontface, test, max, desiredWidth)
}
return found;
}
Upvotes: 6
Reputation: 8400
this will only apply to a small set of circumstances but if provided a perfect answer to the problem for me. The last [optional] parameter to fillText is maxWidth, so
ctx.fillText('long text', x, y, maxWidth);
Will render 'long text' at x,y squashing the result to maxWidth.
For really long text this produces absolutely awful results but for the odd string that was just exceeding your maxWidth it can be a very simple god-send.
Upvotes: 3
Reputation: 3540
Add the maxWidth
Parameter to your context.textfill
$(document).ready(function () {
var canvas = $('#myCanvas')[0];
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 0, 0);
};
imageObj.src = "http://dummyimage.com/282x177/FFF/FFF";
$('#nametag').bind('change keyup input', updateCanvas);
$('#line2').bind('click', updateCanvas);
$('#line3').bind('click', updateCanvas);
$('#line4').bind('click', updateCanvas);
function updateCanvas() {
var maxWith = canvas.width;
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(imageObj, 0, 0);
context.textAlign = "center";
context.font = "bold 18pt Arial";
context.fillText($('#line1').val(), canvas.width * 0.5, 70, maxWith);
context.font = "12pt Arial";
context.fillText($('#line2').val(), canvas.width * 0.5, 90, maxWith);
context.fillText($('#line3').val(), canvas.width * 0.5, 120, maxWith);
context.fillText($('#line4').val(), canvas.width * 0.5, 140, maxWith);
}
});
Upvotes: 6