在HTML5画布上绘制旋转文本


69

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

  • 逆时针旋转文本90度以标记y轴
  • 逆时针旋转文本90度以在垂直条形图上标记条形
  • 任意旋转文本以标记折线图上的线

任何指针将不胜感激。


您是否在考虑研究现有的图形解决方案,而不是尝试构建自己的图形解决方案?flot(code.google.com/p/flot)是使用画布的一个示例。
Bartek

Answers:


41

就像其他人提到的那样,您可能想看看重用现有的图形解决方案,但是旋转文本并不是很困难。(对我而言)有些令人困惑的一点是,您旋转整个上下文然后在其上进行绘制:

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

角度是弧度。该代码取自该示例,我相信该示例是为MDC画布教程转换部分制作的

请参阅下面的答案以获取更完整的解决方案。


4
radians = (angle * Math.PI / 180)
ashleedawg

130

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

我认为翻译和转换为上下文是操纵覆盖在画布上的坐标网格。默认情况下,原点(0,0)从画布的左上角开始。X从左到右增加,Y从上到下增加。如果您用左手的食指和拇指做一个“ L”,并在您的拇指向下的情况下将其伸出在您的面前,则拇指将指向增加Y的方向,而食指将指向该方向我知道它是基本的,但在考虑平移和旋转时会有所帮助。原因如下:

翻译上下文时,将坐标网格的原点移动到画布上的新位置。旋转上下文时,请考虑将用左手制作的“ L”沿顺时针方向旋转,以您指定的弧度(相对于原点)的角度表示的数量。当您描边文本或填充文本时,请指定相对于新对齐的轴的坐标。要调整文本的方向以便从下到上可读,您可以将其转换为要在其下方开始标签的位置,旋转-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()时的状态-方便将内容恢复为“正常”状态。



3
很棒的翻译/旋转说明。+1
罗马

9
如果您想将角度设置为特定的角度,则可以执行以下操作:context.rotate(angle * (Math.PI / 180));
PaulBGD 2014年

为了进一步说明...:-Math.PI / 2.0上面的旋转代码使您的文本逆时针旋转...大概很多人都想这样做,以便在图形的左侧绘制垂直文本,所以...:移至底部在图形的左上角(不进行填充或其他操作),进行-Math.PI / 2.0旋转,以X位置(旋转时为垂直)在图形中心Y处居中绘制,Y位置的文本大小为负一半。(您也可以事先翻译一个额外的正半个文本大小,但这更容易理解imo。)
Andrew

28

虽然这是对上一个答案的跟进,但它(希望)有所增加。

我主要要澄清的是,通常我们会想到绘制类似 draw a rectangle at 10, 3

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

另一个要点是文本​​的对齐方式。最容易在0,0处绘制文本,因此使用正确的对齐方式可以使我们无需测量文本宽度。

我们仍应将文本移动一定量以使其垂直居中,但不幸的是,画布没有很好的行高支持,因此这是一个猜测并检查一下(如果有更好的地方请纠正我)。

我创建了3个示例,这些示例提供了一个点和一个具有3个对齐方式的文本,以显示屏幕上实际的点是字体的位置。

在此处输入图片说明

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,除了我们稍微移动以使文本居中于该点附近


5

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

首先,文字的高度等于像素字体大小。现在,这是我前一段时间阅读的内容,并且已经在我的计算中得出。我不确定这是否适用于所有字体,但似乎可以与无衬线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 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;">


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.