[javascript] Drawing rotated text on a HTML5 canvas

Part of a web application I'm developing requires me to create bar graphs to display various information. I figured, if the user's browser is capable, I would draw them using the HTML5 canvas element. I have no problem drawing lines and bars for my graphs, but when it comes to labeling the axes, the bars, or the lines I ran into a snag. How do I draw rotated text onto a canvas element so that it lines up with the item it is labeling? A couple examples include:

  • Rotate text 90 degrees counter clockwise to label the y-axis
  • Rotate text 90 degrees counter clockwise to label bars on a vertical bar graph
  • Rotate text an arbitrary amount to label lines on a line graph

Any pointers would be appreciated.

This question is related to javascript html canvas

The answer is


Like others have mentioned, you probably want to look at reusing an existing graphing solution, but rotating text isn't too difficult. The somewhat confusing bit (to me) is that you rotate the whole context and then draw on it:

ctx.rotate(Math.PI*2/(i*6));

The angle is in radians. The code is taken from this example, which I believe was made for the transformations part of the MDC canvas tutorial.

Please see the answer below for a more complete solution.


Funkodebat posted a great solution which I have referenced many times. Still, I find myself writing my own working model each time I need this. So, here is my working model... with some added clarity.

First of all, the height of the text is equal to the pixel font size. Now, this was something I read a while ago, and it has worked out in my calculations. I'm not sure if this works with all fonts, but it seems to work with Arial, sans-serif.

Also, to make sure that you fit all of the text in your canvas (and don't trim the tails off of your "p"'s) you need to set context.textBaseline*.

You will see in the code that we are rotating the text about its center. To do this, we need to set context.textAlign = "center" and the context.textBaseline to bottom, otherwise, we trim off parts of our text.

Why resize the canvas? I usually have a canvas that isn't appended to the page. I use it to draw all of my rotated text, then I draw it onto another canvas which I display. For example, you can use this canvas to draw all of the labels for a chart (one by one) and draw the hidden canvas onto the chart canvas where you need the label (context.drawImage(hiddenCanvas, 0, 0);).

IMPORTANT NOTE: Set your font before measuring your text, and re-apply all of your styling to the context after resizing your canvas. A canvas's context is completely reset when the canvas is resized.

Hope this helps!

_x000D_
_x000D_
var c = document.getElementById("myCanvas");_x000D_
var ctx = c.getContext("2d");_x000D_
var font, text, x, y;_x000D_
_x000D_
text = "Mississippi";_x000D_
_x000D_
//Set font size before measuring_x000D_
font = 20;_x000D_
ctx.font = font + 'px Arial, sans-serif';_x000D_
//Get width of text_x000D_
var metrics = ctx.measureText(text);_x000D_
//Set canvas dimensions_x000D_
c.width = font;//The height of the text. The text will be sideways._x000D_
c.height = metrics.width;//The measured width of the text_x000D_
//After a canvas resize, the context is reset. Set the font size again_x000D_
ctx.font = font + 'px Arial';_x000D_
//Set the drawing coordinates_x000D_
x = font/2;_x000D_
y = metrics.width/2;_x000D_
//Style_x000D_
ctx.fillStyle = 'black';_x000D_
ctx.textAlign = 'center';_x000D_
ctx.textBaseline = "bottom";_x000D_
//Rotate the context and draw the text_x000D_
ctx.save();_x000D_
ctx.translate(x, y);_x000D_
ctx.rotate(-Math.PI / 2);_x000D_
ctx.fillText(text, 0, font / 2);_x000D_
ctx.restore();
_x000D_
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
_x000D_
_x000D_
_x000D_


Posting this in an effort to help others with similar problems. I solved this issue with a five step approach -- save the context, translate the context, rotate the context, draw the text, then restore the context to its saved state.

I think of translations and transforms to the context as manipulating the coordinate grid overlaid on the canvas. By default the origin (0,0) starts in the upper left hand corner of the canvas. X increases from left to right, Y increases from top to bottom. If you make an "L" w/ your index finger and thumb on your left hand and hold it out in front of you with your thumb down, your thumb would point in the direction of increasing Y and your index finger would point in the direction of increasing X. I know it's elementary, but I find it helpful when thinking about translations and rotations. Here's why:

When you translate the context, you move the origin of the coordinate grid to a new location on the canvas. When you rotate the context, think of rotating the "L" you made with your left hand in a clockwise direction the amount indicated by the angle you specify in radians about the origin. When you strokeText or fillText, specify your coordinates in relation to the newly aligned axes. To orient your text so it's readable from bottom to top, you would translate to a position below where you want to start your labels, rotate by -90 degrees and fill or strokeText, offsetting each label along the rotated x axis. Something like this should work:

 context.save();
 context.translate(newx, newy);
 context.rotate(-Math.PI/2);
 context.textAlign = "center";
 context.fillText("Your Label Here", labelXposition, 0);
 context.restore();

.restore() resets the context back to the state it had when you called .save() -- handy for returning things back to "normal".



While this is sort of a follow up to the previous answer, it adds a little (hopefully).

Mainly what I want to clarify is that usually we think of drawing things like draw a rectangle at 10, 3.

So if we think about that like this: move origin to 10, 3, then draw rectangle at 0, 0. Then all we have to do is add a rotate in between.

Another big point is the alignment of the text. It's easiest to draw the text at 0, 0, so using the correct alignment can allow us to do that without measuring the text width.

We should still move the text by an amount to get it centered vertically, and unfortunately canvas does not have great line height support, so that's a guess and check thing ( correct me if there is something better ).

I've created 3 examples that provide a point and a text with 3 alignments, to show what the actual point on the screen is where the font will go.

enter image description here

var font, lineHeight, x, y;

x = 100;
y = 100;
font = 20;
lineHeight = 15; // this is guess and check as far as I know
this.context.font = font + 'px Arial';


// Right Aligned
this.context.save();
this.context.translate(x, y);
this.context.rotate(-Math.PI / 4);

this.context.textAlign = 'right';
this.context.fillText('right', 0, lineHeight / 2);

this.context.restore();

this.context.fillStyle = 'red';
this.context.fillRect(x, y, 2, 2);


// Center
this.context.fillStyle = 'black';
x = 150;
y = 100;

this.context.save();
this.context.translate(x, y);
this.context.rotate(-Math.PI / 4);

this.context.textAlign = 'center';
this.context.fillText('center', 0, lineHeight / 2);

this.context.restore();

this.context.fillStyle = 'red';
this.context.fillRect(x, y, 2, 2);


// Left
this.context.fillStyle = 'black';
x = 200;
y = 100;

this.context.save();
this.context.translate(x, y);
this.context.rotate(-Math.PI / 4);

this.context.textAlign = 'left';
this.context.fillText('left', 0, lineHeight / 2);

this.context.restore();

this.context.fillStyle = 'red';
this.context.fillRect(x, y, 2, 2);

The line this.context.fillText('right', 0, lineHeight / 2); is basically 0, 0, except we move slightly for the text to be centered near the point


Here's an HTML5 alternative to homebrew: http://www.rgraph.net/ You might be able to reverse engineer their methods....

You might also consider something like Flot (http://code.google.com/p/flot/) or GCharts: (http://www.maxb.net/scripts/jgcharts/include/demo/#1) It's not quite as cool, but fully backwards compatible and scary easy to implement.


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to canvas

How to make canvas responsive How to fill the whole canvas with specific color? Use HTML5 to resize an image before upload Convert canvas to PDF Scaling an image to fit on canvas Split string in JavaScript and detect line break Get distance between two points in canvas canvas.toDataURL() SecurityError Converting Chart.js canvas chart to image using .toDataUrl() results in blank image Chart.js canvas resize