在HTML5画布上绘制旋转文本

Spa*_*ile 61 javascript html5 canvas

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

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

任何指针将不胜感激.

小智 119

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

我想到翻译和转换到上下文操作覆盖在画布上的坐标网格.默认情况下,原点(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();
Run Code Online (Sandbox Code Playgroud)

.restore()将上下文重置回你调用.save()时的状态 - 方便返回"正常"状态.


  • 如果你想将角度设置为特定的角度,你可以这样做:`context.rotate(angle*(Math.PI/180));` (7认同)
  • 伟大的翻译/旋转解释.+1 (2认同)

rob*_*rtc 36

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

ctx.rotate(Math.PI*2/(i*6));
Run Code Online (Sandbox Code Playgroud)

角度是弧度.代码来自这个例子,我相信这是为MDC画布教程转换部分做的.

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

  • `弧度 = (角度 * Math.PI / 180)` (4认同)

Fun*_*bat 26

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

我想澄清的主要是通常我们会想到绘制的东西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);
Run Code Online (Sandbox Code Playgroud)

该线this.context.fillText('right', 0, lineHeight / 2);基本上是0, 0,除了我们略微移动文本在点附近居中


Web*_*rer 5

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();
Run Code Online (Sandbox Code Playgroud)
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Run Code Online (Sandbox Code Playgroud)