I'm looking to have text inside a circle and have the text be contained by it like this:
All I've been able to do is position the text inside but it is keeping a square container, not a circle.
Wrapping text inside a circle
A circle chord is a line segment with its endpoints on the circle.
To fit text horizontally inside a circle you need to calculate widths of horizontal chords in that circle.
// r == the circle radius
// h == the vertical distance of the horizontal chord from the top of the circle
var chordWidth = 2*Math.sqrt(h*(2*r-h));
Calculate the width used by any text using context.measureText:
var textWidth = context.measureText("Measure this text").width;
With these calculations you can "word wrap" your text onto each chord.
You do that by adding words to a line of text until the accumulated text phrase is wider than the chord width. Then start the next line of text with the word that exceeded the chord width.
Here's code and a Demo: http://jsfiddle.net/m1erickson/jcb3D/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var text = "'Twas the night before Christmas, when all through the house, Not a creature was stirring, not even a mouse.";
var font = "12pt verdana";
var textHeight = 15;
var lineHeight = textHeight + 5;
var lines = [];
var cx = 150;
var cy = 150;
var r = 100;
initLines();
wrapText();
ctx.beginPath();
ctx.arc(cx, cy, r, 0, Math.PI * 2, false);
ctx.closePath();
ctx.strokeStyle = "skyblue";
ctx.lineWidth = 2;
ctx.stroke();
// pre-calculate width of each horizontal chord of the circle
// This is the max width allowed for text
function initLines() {
for (var y = r * .90; y > -r; y -= lineHeight) {
var h = Math.abs(r - y);
if (y - lineHeight < 0) {
h += 20;
}
var length = 2 * Math.sqrt(h * (2 * r - h));
if (length && length > 10) {
lines.push({
y: y,
maxLength: length
});
}
}
}
// draw text on each line of the circle
function wrapText(){
var i=0;
var words=text.split(" ");
while(i<lines.length && words.length>0){
line=lines[i++];
var lineData=calcAllowableWords(line.maxLength,words);
ctx.fillText(lineData.text, cx-lineData.width/2, cy-line.y+textHeight);
words.splice(0,lineData.count);
};
}
// calculate how many words will fit on a line
function calcAllowableWords(maxWidth,words){
var wordCount=0;
var testLine="";
var spacer="";
var fittedWidth=0;
var fittedText="";
ctx.font=font;
for(var i=0;i<words.length; i++){
testLine+=spacer+words[i];
spacer=" ";
var width=ctx.measureText(testLine).width;
if(width>maxWidth){
return({
count:i,
width:fittedWidth,
text:fittedText
});
}
fittedWidth=width;
fittedText=testLine;
}
// always return the last set of words
return({
count:words.length,
width:width,
text:testLine,
});
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>