我正在开发的 Web 应用程序的一部分需要我创建条形图以显示各种信息。如果用户的浏览器支持,我会使用 HTML5 canvas 元素来绘制它们。在绘制图表的线和条时没有问题,但当涉及到标记轴、条或线时,我遇到了一些麻烦。如何将旋转的文本绘制到画布元素上,使其与所标记的项对齐?几个示例包括:
- 将文本逆时针旋转 90 度以标记 y 轴
- 将文本逆时针旋转 90 度以标记垂直柱状图上的条形
- 旋转文本任意角度以标记折线图上的线
如有指导意见,敬请赐教。
我正在开发的 Web 应用程序的一部分需要我创建条形图以显示各种信息。如果用户的浏览器支持,我会使用 HTML5 canvas 元素来绘制它们。在绘制图表的线和条时没有问题,但当涉及到标记轴、条或线时,我遇到了一些麻烦。如何将旋转的文本绘制到画布元素上,使其与所标记的项对齐?几个示例包括:
如有指导意见,敬请赐教。
分享这篇内容是为了帮助其他遇到类似问题的人。我通过五步法解决了这个问题--保存上下文, 翻译上下文, 旋转上下文, 绘制文本, 然后将上下文恢复到保存的状态。
我认为将上下文进行翻译和变换就像是在画布上叠加坐标网格一样。默认情况下,原点(0,0)位于画布的左上角。X轴从左到右增加,Y轴从上到下增加。如果你用左手食指和拇指做出一个“L”形并举在你面前,大拇指朝下,则大拇指指向递增的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() 时的状态,有助于将事物恢复为“正常”。
context.rotate(angle * (Math.PI / 180));
- PaulBGD-Math.PI / 2.0
旋转代码会将您的文本逆时针旋转...大概很多人都想在图表左侧绘制垂直文本,所以...:将其翻译到图表的左下角(没有填充或任何东西),进行 -Math.PI / 2.0
旋转,以图表中心 Y 为 X 位置进行居中绘制(当旋转时为垂直),并将文本大小的负一半用于 Y 位置。(您也可以事先将额外的正文本大小的一半进行平移,但我认为这样更容易理解。) - Andrew虽然这有点是前一个答案的跟进,但增加了一点(希望如此)。
主要想澄清的是,通常我们会考虑像“在10, 3处画一个矩形”的绘图方式。
因此,如果我们把它看作是:“将原点移动到10, 3”,然后“在0, 0处画一个矩形”。 那么我们只需要在两者之间添加旋转即可。
另一个重要的点是文本的对齐方式。最好将文本绘制在0, 0处,因此使用正确的对齐方式可以让我们无需测量文本宽度即可实现。
我们仍应该通过一定的偏移量移动文本,以使其垂直居中,并且不幸的是,画布没有很好的行高支持,因此这是一个猜测和检查的过程(如果有更好的方法,请纠正我)。
我创建了三个示例,其中提供了一个点和三种对齐方式的文本,以显示字体将放置在屏幕上的实际点。
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
,只是我们稍微移动一下文本位置,使其在点附近居中显示。
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) 这样的东西。虽然不太酷,但是完全向后兼容且非常容易实现。