18

How do I create circle text (text in a circle shape) with canvas?

Like this

seymar
  • 3,993
  • 6
  • 25
  • 30

6 Answers6

23

Letters should now be properly oriented:

CanvasRenderingContext2D.prototype.fillTextCircle = function(text,x,y,radius,startRotation){
   var numRadsPerLetter = 2*Math.PI / text.length;
   this.save();
   this.translate(x,y);
   this.rotate(startRotation);

   for(var i=0;i<text.length;i++){
      this.save();
      this.rotate(i*numRadsPerLetter);

      this.fillText(text[i],0,-radius);
      this.restore();
   }
   this.restore();
}

Sample usage:

var ctx = document.getElementById('canvas').getContext('2d');
ctx.font = "bold 30px Serif";
ctx.fillTextCircle("Circle Text ",150,150,75,Math.PI / 2);

The extra space at the end of the string adds some extra padding.

Sample output:

Sample Output

cmptrgeekken
  • 8,052
  • 3
  • 29
  • 35
  • I already thought about making this, thanks for the start. It still needs some improvements I see :P – seymar May 19 '11 at 17:03
  • Sure does. Was just a quickie code-up as a proof of concept :) – cmptrgeekken May 19 '11 at 17:04
  • +1 for the example! I have amended cmptrgeekken's code in order to rotate the letters too, so now we have a working example that has come "full circle!" – Simon Sarris May 19 '11 at 17:26
  • cmptrgeekken didn't mention, but he has updated his code to do the same effect mine does, but with a more generalized method. Use his as a starting point. Also, here: http://jsfiddle.net/D5JWa/5/ You gotta remember to call init() after you make it, and remember that strokeStyle is not a function (changed it to ='s) – Simon Sarris May 19 '11 at 17:36
  • Yeah, I realized that the position in the `ctx.fillText()` call actually determines the orientation of the letters. With that in mind, you can play around with various positioning to achieve whatever letter orientation you would like. – cmptrgeekken May 19 '11 at 17:38
8

It can technically be done, but there is no built in way. You'd have to calculate an arc, and draw each letter individually along that arc, figuring out the angle and positioning yourself.

Many people end up making their own methods (like the above) for text. Heck, multiline text can't even be done by default!

EDIT: Here is a working example, piggybacking off of cmptrgeekken's work. If you upvote me, upvote him too :P

http://jsfiddle.net/c3Y8M/1/

What it looks like:

Sample

Simon Sarris
  • 62,212
  • 13
  • 141
  • 171
  • Thanks for the demo. So if I don't want the letters to spread out across the circle, do I just multiply the `numDegreesPerLetter` by a ratio less than 1? I've adapted your codes here https://jsfiddle.net/thoakun/1zmL9rwd/ – bytrangle Sep 14 '21 at 07:53
6

On my blog, I take a fairly close look at creating circular text using HTML5 Canvas:

html5graphics.blogspot.com

In the example, options include rounded text alignment (left, center and right) from a given angle, inward and outward facing text, kerning (adjustable gap between characters) and text inside or outside the radius.

There is also a jsfiddle with a working example.

It is as follows:

document.body.appendChild(getCircularText("ROUNDED TEXT LOOKS BEST IN CAPS!", 250, 0, "center", false, true, "Arial", "18pt", 2));

function getCircularText(text, diameter, startAngle, align, textInside, inwardFacing, fName, fSize, kerning) {
    // text:         The text to be displayed in circular fashion
    // diameter:     The diameter of the circle around which the text will
    //               be displayed (inside or outside)
    // startAngle:   In degrees, Where the text will be shown. 0 degrees
    //               if the top of the circle
    // align:        Positions text to left right or center of startAngle
    // textInside:   true to show inside the diameter. False to show outside
    // inwardFacing: true for base of text facing inward. false for outward
    // fName:        name of font family. Make sure it is loaded
    // fSize:        size of font family. Don't forget to include units
    // kearning:     0 for normal gap between letters. positive or
    //               negative number to expand/compact gap in pixels
 //------------------------------------------------------------------------

    // declare and intialize canvas, reference, and useful variables
    align = align.toLowerCase();
    var mainCanvas = document.createElement('canvas');
    var ctxRef = mainCanvas.getContext('2d');
    var clockwise = align == "right" ? 1 : -1; // draw clockwise for aligned right. Else Anticlockwise
    startAngle = startAngle * (Math.PI / 180); // convert to radians

    // calculate height of the font. Many ways to do this
    // you can replace with your own!
    var div = document.createElement("div");
    div.innerHTML = text;
    div.style.position = 'absolute';
    div.style.top = '-10000px';
    div.style.left = '-10000px';
    div.style.fontFamily = fName;
    div.style.fontSize = fSize;
    document.body.appendChild(div);
    var textHeight = div.offsetHeight;
    document.body.removeChild(div);

    // in cases where we are drawing outside diameter,
    // expand diameter to handle it
    if (!textInside) diameter += textHeight * 2;

    mainCanvas.width = diameter;
    mainCanvas.height = diameter;
    // omit next line for transparent background
    mainCanvas.style.backgroundColor = 'lightgray'; 
    ctxRef.fillStyle = 'black';
    ctxRef.font = fSize + ' ' + fName;

    // Reverse letters for align Left inward, align right outward 
    // and align center inward.
    if (((["left", "center"].indexOf(align) > -1) && inwardFacing) || (align == "right" && !inwardFacing)) text = text.split("").reverse().join(""); 

    // Setup letters and positioning
    ctxRef.translate(diameter / 2, diameter / 2); // Move to center
    startAngle += (Math.PI * !inwardFacing); // Rotate 180 if outward
    ctxRef.textBaseline = 'middle'; // Ensure we draw in exact center
    ctxRef.textAlign = 'center'; // Ensure we draw in exact center

    // rotate 50% of total angle for center alignment
    if (align == "center") {
        for (var j = 0; j < text.length; j++) {
            var charWid = ctxRef.measureText(text[j]).width;
            startAngle += ((charWid + (j == text.length-1 ? 0 : kerning)) / (diameter / 2 - textHeight)) / 2 * -clockwise;
        }
    }

    // Phew... now rotate into final start position
    ctxRef.rotate(startAngle);

    // Now for the fun bit: draw, rotate, and repeat
    for (var j = 0; j < text.length; j++) {
        var charWid = ctxRef.measureText(text[j]).width; // half letter
        // rotate half letter
        ctxRef.rotate((charWid/2) / (diameter / 2 - textHeight) * clockwise); 
        // draw the character at "top" or "bottom" 
        // depending on inward or outward facing
        ctxRef.fillText(text[j], 0, (inwardFacing ? 1 : -1) * (0 - diameter / 2 + textHeight / 2));

        ctxRef.rotate((charWid/2 + kerning) / (diameter / 2 - textHeight) * clockwise); // rotate half letter
    }

    // Return it
    return (mainCanvas);
}
Jralford
  • 211
  • 3
  • 6
4

It's my modification of this: http://jsfiddle.net/Brfp3/3/ But feature allows you to display text clockwise and counterclockwise.

function textCircle(text,x,y,radius,space,top){
           space = space || 0;
           var numRadsPerLetter = (Math.PI - space * 2) / text.length;
           ctx.save();
           ctx.translate(x,y);
           var k = (top) ? 1 : -1; 
           ctx.rotate(-k * ((Math.PI - numRadsPerLetter) / 2 - space));
           for(var i=0;i<text.length;i++){
              ctx.save();
              ctx.rotate(k*i*(numRadsPerLetter));
              ctx.textAlign = "center";
             ctx.textBaseline = (!top) ? "top" : "bottom";
             ctx.fillText(text[i],0,-k*(radius));
              ctx.restore();
           }
           ctx.restore();
        }

Sample usage:

ctx.font = "bold 30px Courier";
textCircle("Half circle Text",150,150,75,Math.PI/12,1);
textCircle("Half circle Text",150,150,75,Math.PI/12);
4

A version in which the size of characters is counted. The spaces between the letters are therefore always the same size.

function drawTextAlongArc(context, str, centerX, centerY, radius, angle) {
    var len = str.length, s, letterAngle;

    context.save();
    context.textAlign = 'center';
    context.translate(centerX, centerY);
    context.rotate(angle + Math.PI / 2);

    for (var n = 0; n < len; n++) {
        s = str[n];
        letterAngle = 0.5*(context.measureText(s).width / radius);

        context.rotate(letterAngle);
        context.save();

        context.translate(0, -radius);
        context.fillText(s, 0, 0);
        context.restore();

        context.rotate(letterAngle);
    }
    context.restore();
}
Servus7
  • 358
  • 4
  • 9
1

CircleType.js doesn't use canvas but achieves the same effect: http://circletype.labwire.ca - also works well in fluid layouts.

Community
  • 1
  • 1
peterhry
  • 1,120
  • 12
  • 16