HTML5 Canvas - Solving the Problem of Inverted Text When Y-Axis is Inverted

javascript
Published on December 4, 2016

The Problem

Conventionally the origin of the canvas element is its top-left corner. The positive X-axis is from its origin towards the right, and the positive Y-axis is from its origin towards the bottom.

However there are situations where we have to change the origin, and also the direction of the axes. An example could be a mathematical curve or a chart. In mathematics, the cartesian co-ordinate system is a little different from the canvas co-ordinate system. The direction of the X-axis of both is the same, but the direction of their Y-axis is opposite.

Consider the following situation where the desired origin and Y-axis direction is different from the conventional canvas :

To have the desired effect we would do the following:

  1. To shift the origin we would perform a canvas.translate(x, y) on the canvas.
  2. To make the Y-axis opposite we would perform a canvas.scale(1, -1) on the canvas.

Now it would seem that we have achieved our effect. Drawing lines and curves on the canvas becomes easy using the new co-ordinate system. However if try we try to add text or even images to the canvas, we will find that they are inverted / flipped. The situation is shown below in the canvas (2 straight lines denote the new axes as shown in the figure above) :

Just for reference these are the codes for the above drawn canvas :

<!-- <canvas id="first-canvas" width="400" height="300"></canvas> --> var canvas = document.getElementById("first-canvas"); var ctx1 = canvas.getContext("2d"); ctx1.beginPath(); ctx1.lineWidth = 1; ctx1.strokeStyle = "#CCCCCC"; // Y-axis drawn for reference // 0.5px adjustment to get crisp straight lines ctx1.moveTo(150.5, 0); ctx1.lineTo(150.5, 300); ctx1.stroke(); // X-axis drawn for reference ctx1.moveTo(0, 150.5); ctx1.lineTo(400, 150.5); ctx1.stroke(); // Origin translated to (150, 150) ctx1.translate(150, 150); // Y-axis inverted ctx1.scale(1, -1); // Sample line drawn ctx1.beginPath(); ctx1.lineWidth = 2; ctx1.strokeStyle = "red" ctx1.moveTo(80, 80); ctx1.lineTo(130, 130); ctx1.stroke(); // Text Added ctx1.font = '16px Arial'; ctx1.textAlign = 'start'; ctx1.fillText('Sample Text', 50, 50);

However there is a trick through which you can still flip the Y-axis and still get the correct direction of the text or image.

The Solution

As told before you have already translated the origin and inverted the Y-axis according to the mathematical co-ordinate system. Now when you are adding a text or an image in the canvas, do the following :

  1. Save a context of the canvas first through context.save.
  2. Translate the canvas again to the point where you were supposed to add the text previously.
  3. Invert the Y-axis once again.
  4. Add the text to the current origin of the canvas context.fillText('Sample Text', 0, 0)
  5. Restore back the canvas to the previous context through context.restore.

The important point is to note that the canvas is translated again to the point where the text was supposed to be added. Then Y-axis was inverted again. And then text is added to the origin (0,0).

A disadvantage is that this has to be done for every text or image that is to be added. In case of HTML5 canvas games, using this a lot may hamper the performance.

Demo

Translated origin and flipped Y-axis, but correct orientation of text (or image) :

<!-- <canvas id="second-canvas" width="400" height="300"></canvas> --> var canvas = document.getElementById("second-canvas"); var ctx2 = canvas.getContext("2d"); // 0.5px adjustment to get crisp straight lines // Y-axis drawn for reference ctx2.beginPath(); ctx2.lineWidth = 1; ctx2.strokeStyle = "#CCCCCC"; // X-axis drawn for reference ctx2.moveTo(150.5, 0); ctx2.lineTo(150.5, 300); ctx2.stroke(); ctx2.moveTo(0, 150.5); ctx2.lineTo(400, 150.5); ctx2.stroke(); // Origin translated to (150, 150) ctx2.translate(150, 150); // Y-axis inverted ctx2.scale(1, -1); // Sample line drawn ctx2.beginPath(); ctx2.lineWidth = 2; ctx2.strokeStyle = "red" ctx2.moveTo(80, 80); ctx2.lineTo(130, 130); ctx2.stroke(); // Before adding text we save the context // Then translate to the point where text was supposed to be added (50, 50) // Then invert Y-axis again ctx2.save(); ctx2.translate(50, 50); ctx2.scale(1, -1); // Text Added at the origin of the newly transformed canvas ctx2.font = '16px Arial'; ctx2.textAlign = 'start'; ctx2.fillText('Sample Text', 0, 0); // Context restored back ctx2.restore();
In this Tutorial