在 HTML5画布上绘制旋转文本

我正在开发的 Web 应用程序的一部分要求我创建条形图来显示各种信息。我想,如果用户的浏览器是能够的,我会画他们使用 HTML5画布元素。我可以为我的图画线条,但是在标记轴、条或者线条的时候,我遇到了一个障碍。如何将旋转的文本绘制到画布元素上,使其与正在标记的项对齐?一些例子包括:

  • 旋转文本90度计数器 顺时针标记 y 轴
  • 旋转文本90度计数器 以顺时针方向在垂直条上标记条 柱状图柱状图
  • 将任意数量的文本旋转到 线图上的标记线

有任何建议都可以。

117733 次浏览

这里有一个 HTML5替代自制程序: http://www.rgraph.net/你也许能够逆向工程他们的方法..。

你也可以考虑像 Flot (http://code.google.com/p/flot/)或者 GCharts: (http://www.maxb.net/scripts/jgcharts/include/demo/#1)这样的东西,它不是很酷,但是完全向后兼容并且很容易实现。

正如其他人提到的,您可能希望重用现有的图形解决方案,但是旋转文本并不太困难。(对我来说)有点令人困惑的是,你旋转整个上下文,然后在上面画:

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

代码是 取自这个例子我相信这是为 MDC 画布教程的转换部分做的。

请参阅 下面的答案了解更完整的解决方案。

发布这篇文章是为了帮助其他有类似问题的人。我通过五个步骤解决了这个问题——保存上下文、翻译上下文、旋转上下文、绘制文本,然后将上下文恢复到保存的状态。

我认为翻译和转换到上下文就像操作覆盖在画布上的坐标网格一样。默认情况下,原点(0,0)从画布的左上角开始。X 从左到右增加,Y 从上到下增加。如果你在你的左手上画一个“ L”,你的食指和拇指放在你的前面,拇指会指向增加 Y 的方向,而你的食指会指向增加 X 的方向。我知道这是基本的,但是我发现在思考平移和旋转时它很有帮助。原因如下:

翻译上下文时,将坐标网格的原点移动到画布上的新位置。当你旋转上下文的时候,想象一下你用左手在一个顺时针方向上旋转“ L”,这个“ L”是用弧度表示的。当您使用 strokeText 或 filText 时,请指定与新对齐的轴相关的坐标。为了让你的文本从下到上都可读,你需要将文本翻译到下面的位置,在这个位置开始标签,旋转 -90度,填充或者笔画文本,沿着旋转的 x 轴偏移每个标签。这种方法应该可行:

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

.Return ()将上下文重置回调用时的状态。Save ()——方便返回“正常”。


虽然这是对前一个答案的一种跟进,但它增加了一点(希望如此)。

主要我想澄清的是,通常我们认为像 draw a rectangle at 10, 3这样的东西。

如果我们这样想: move origin to 10, 3,然后是 draw rectangle at 0, 0。 然后我们要做的就是在两者之间加一个旋转。

另一个重要的问题是文本的对齐。在0,0处绘制文本是最简单的,因此使用正确的对齐方式可以让我们在不测量文本宽度的情况下绘制文本。

我们仍然应该移动文本的数量,以使其垂直居中,不幸的是画布没有很好的行高度支持,所以这是一个猜测和检查的事情(纠正我,如果有更好的东西)。

我已经创建了3个示例,它们提供了一个点和一个具有3种对齐方式的文本,以显示屏幕上的实际点是字体的去向。

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);

this.context.fillText('right', 0, lineHeight / 2);基本上是 0, 0,除了我们稍微移动的文本是中心附近的点

Funkodebat 发布了一个伟大的解决方案,我已经参考了很多次。 尽管如此,每当我需要这个时,我都会编写自己的工作模型。 所以,这是我的工作模式... 有一些额外的清晰度。

首先,文本的高度等于像素字体大小。 这是我前段时间读到的东西,在我的计算中得到了验证。我不确定这是否适用于所有字体,但它似乎适用于 Arial,sans-serif。

同样,为了确保你的画布能够容纳所有的文字(不要修剪掉“ p”的尾部) ,你需要设置 TextBaseline *

您将在代码中看到我们正在将文本围绕其中心旋转。 要做到这一点,我们需要设置 TextAlign = “ center”TextBaseline底部,否则,我们修剪了我们的文本的部分。

为什么要调整画布的大小? 我通常有一个不附加在页面上的画布。我用它来画我所有的旋转文本,然后我把它画到另一个画布,我显示。 例如,您可以使用这个画布来绘制一个图表的所有标签(一个接一个) ,并将隐藏的画布绘制到需要标签的图表画布(context.drawImage(hiddenCanvas, 0, 0);)上。

重要提示: 在测量文本前设置字体,并在调整画布大小后重新应用所有样式。当调整画布的大小时,画布的上下文将完全重置。

希望这个能帮上忙!

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var font, text, x, y;


text = "Mississippi";


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