在 HTML5 画布上绘制旋转文本

IT技术 javascript html canvas
2021-03-17 10:56:15

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

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

任何指针将不胜感激。

5个回答

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

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

平移上下文时,会将坐标网格的原点移动到画布上的新位置。当您旋转上下文时,请考虑将您用左手制作的“L”沿顺时针方向旋转,旋转量由您指定的关于原点的弧度角指示。当您使用strokeText 或fillText 时,请指定与新对齐的轴相关的坐标。要调整文本方向以使其从下到上可读,您需要将位置转换为下方要开始标签的位置,旋转 -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();

.restore() 将上下文重置回它在调用 .save() 时的状态——方便将事情恢复到“正常”。


很棒的翻译/旋转解释。+1
2021-04-27 10:56:15
进一步澄清...:-Math.PI / 2.0上面旋转代码将您的文本逆时针旋转...想必很多人都希望在图形的左侧绘制垂直文本,所以...:转换到底部图形的左角(无填充或任何东西),进行-Math.PI / 2.0旋转,以图形的 Y 中心为中心绘制 X 位置(旋转时为垂直),Y 位置的文本大小为负一半。(您也可以提前翻译一半的文本大小,但这更容易理解 imo。)
2021-04-27 10:56:15
我个人不需要翻译,但同样感谢!
2021-05-12 10:56:15
如果你想将角度设置为特定的东西,你可以这样做: context.rotate(angle * (Math.PI / 180));
2021-05-16 10:56:15

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

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

角度是弧度代码取自这个例子,我相信它是为MDC 画布教程转换部分制作的

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

radians = (angle * Math.PI / 180)
2021-05-02 10:56:15

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

我主要想澄清的是,通常我们会想到绘制诸如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

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

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

此外,为了确保您适合画布中的所有文本(并且不要修剪掉“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;">

这是自制软件的 HTML5 替代方案:http : //www.rgraph.net/ 您也许可以对他们的方法进行逆向工程....

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