在HTML5画布上绘制旋转文本

时间:2010-07-02 17:45:04

标签: javascript html5 canvas

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

  • 将文字旋转90度计数器 顺时针标记y轴
  • 将文字旋转90度计数器 顺时针方向标记垂直条纹 条形图
  • 将文本旋转为任意数量 线图上的标签线

任何指针都会受到赞赏。

5 个答案:

答案 0 :(得分:113)

发布此信息是为了帮助其他人遇到类似问题。我用五步法解决了这个问题 - 保存上下文,翻译上下文,旋转上下文,绘制文本,然后将上下文恢复到其保存状态。

我想到翻译并转换为上下文,因为它操纵了画布上覆盖的坐标网格。默认情况下,原点(0,0)从画布的左上角开始。 X从左向右增加,Y从上到下增加。如果用左手拿着食指和拇指做“L”并用拇指向下握住它,你的拇指会指向增加Y的方向,你的食指会指向方向增加X.我知道这是基本的,但我觉得在考虑翻译和轮换时很有帮助。原因如下:

翻译上下文时,将坐标网格的原点移动到画布上的新位置。旋转上下文时,请考虑使用左手以顺时针方向旋转“L”,即以弧度指定的角度指示的原点。 strokeText或fillText时,请指定与新对齐轴相关的坐标。要定位文本以使其从下到上可读,您可以转换到下面要开始标记的位置,旋转-90度并填充或strokeText,沿旋转的x轴偏移每个标签。这样的事情应该有效:

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

.restore()将上下文重置回调用.save()时的状态 - 方便将事物恢复为“正常”。


答案 1 :(得分:35)

与其他人提到的一样,您可能希望重新使用现有的图形解决方案,但旋转文本并不太难。有点令人困惑的一点(对我而言)是你旋转整个上下文然后在它上面绘制:

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

angle is in radians。代码为taken from this example,我相信是transformations part of the MDC canvas tutorial

请参阅the answer below以获取更完整的解决方案。

答案 2 :(得分:24)

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

我想澄清的主要是通常我们会想到像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,除非我们略微移动文字以便在点附近居中

答案 3 :(得分:0)

以下是自制的HTML5替代方案:http://www.rgraph.net/您可以对其方法进行逆向工程....

您可能还会考虑像Flot(http://code.google.com/p/flot/)或GCharts:http://www.maxb.net/scripts/jgcharts/include/demo/#1这样的东西。它不太酷,但完全向后兼容且易于实现。

答案 4 :(得分:0)

Funkodebat发布了一个很棒的解决方案,我已经引用了很多次。 尽管如此,我发现自己每次需要时都编写自己的工作模型。 所以,这是我的工作模型……更加清晰了。

首先,文本的高度等于像素字体大小。 现在,这是我前一段时间阅读的内容,并且已经在我的计算中得出。我不确定这是否适用于所有字体,但似乎可以与Sanial-serif的Arial一起使用。

此外,要确保您适合画布中的所有文本(并且不修剪“ p”的尾巴),您需要设置 context.textBaseline *

您将在代码中看到我们正在围绕其中心旋转文本。 为此,我们需要将 context.textAlign =“ center” context.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 canvas 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;">