HTML5 Canvas 自绘持久背景






3.80/5 (4投票s)
如何让 HTML5 Canvas 绘制自己的背景“层”。
引言
我需要一个 HTML5 canvas 来显示图表。该图表需要在页面加载时自动计算其大小(基于 canvas 元素的宽度和高度),具有标题,并显示带缩放比例的网格以及 x 和 y 轴图例。最后,也是最重要的是,图表的数据需要每秒更新几次。
由于是动态大小,我无法使用现成的背景图像;我希望 canvas 在页面加载时自行绘制背景。为了提高效率,我不希望每次更新图表线条时都重绘背景(图例和网格)。显然,这需要两层,一层用于背景,另一层更高的图层用于数据。不幸的是,canvas 不支持图层,所以无法采用显而易见/简单的方法。
我四处搜寻,看到了关于使用多个“堆叠”canvas 元素的建议,这些元素使用绝对元素定位和 z-index,但即使这样对于我要做的事情来说也显得过于复杂。然后我找到了 canvas.toDataURL()
方法,并有了一个想法:为什么不在页面加载时让 canvas 动态绘制其背景,通过 toDataURL()
获取该图像,然后将该图像放入 canvas 元素的 background-image 中呢?
结果证明它效果很好;本文介绍了我的解决方案。
随附的示例是该技术的简单实现。(您可以解压缩该示例,并将您的浏览器指向 *index.html*,它将在没有 webserver
的情况下运行)。
这是随附示例的(非动画)屏幕截图

图表的文本和网格线是背景图像的一部分;红色小球会定期重绘以在网格边界内弹跳。
背景
需要对 HTML/CSS/JavaScript 有基本的了解,并且对 HTML5 canvas 有一定的接触。此外,您还需要一个支持 HTML5 canvas 的浏览器,例如 Firefox、IE9 或 Chrome。
Using the Code
基本技术
如上所述,该技术的核心在于 canvas.toDataURL()
// Retrieve the canvas' drawing context
var canvas = document.getElementById("some-canvas-id");
var context = canvas.getContext("2d");
// Draw your background on the canvas
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
// More background stuff, of course....
// Then, once you've fully composed your background, grab it as a base64 PNG
var base64 = canvas.toDataURL();
// .. and stuff that PNG into the element's background
canvas.style.backgroundImage = "url("+base64+")";
// You can now repeatedly clear & redraw the canvas without losing the background.
示例代码
随附的示例包含 4 个文件
- *index.html* - 声明
canvas
元素,在onload()
中进行设置,然后在计时器上运行一些简单的 JavaScript 代码以使小球动画。 - *canvas.js* - 实现我的“
Chart
”类,该类封装了所有 canvas 的背景声明和绘制。 - *canvashelpers.js* - 实现用于在 HTML5 canvas 上绘制“清晰”线条的辅助函数(有关详细信息,请参阅下面的“兴趣点”)。
- *primitives.js* - 实现
Point
和Rect
类,以辅助绘图。
最值得注意的是 Chart.createBackground()
和 Chart.clear()
(都在 *chart.js* 中)createBackground()
绘制我的自定义背景。当然,这会因应用程序而异,但它概述了一些基本思路,这些思路对于大多数用途应该是通用的。clear()
是一个简单的小技巧,对我的动画代码非常有用。事实证明,每当设置 canvas 的高度时,即使将其设置为当前值,也会清除(擦除)canvas 的上下文。
关注点
虽然 HTML5 canvas 通过 moveto()
和 lineto()
以“通常的方式”绘制线条,但它处理这些线条的反锯齿的方式是我以前没有遇到过的。通常,如果您执行诸如 moveto(10,10); lineto(100,10);
之类的操作,您会得到一条清晰的 1 像素高的水平线。但是使用 canvas,这将导致一条“模糊”(跨 2 个像素进行抗锯齿处理)的水平线...这完全出乎我的意料。事实证明,canvas 使用浮点数(而不是 int
)地址空间,为了获得预期的清晰 1 像素线条,您必须使用半点偏移,例如 moveto(10.5, 10.5); lineto(100.5, 10.5);
。
我花了一段时间才理解发生了什么; 此页面很好地解释了这一点。
我的示例代码包含辅助函数 crispLine()
和 crispRect()
(在 *canvashelpers.js* 中),它们自动处理此半点偏移,以产生我期望的清晰线条。
在这个项目的过程中,我对 HTML5 的许多功能(不仅仅是 canvas
)进行了大量的实验,发现 Chrome 拥有最好的支持。Chrome 支持一切(并且效率很高),Firefox 支持大多数内容(虽然不是全部,而且是一个内存占用大户),而 IE9... 仍然是 IE:它支持一些内容,不支持其他内容,做一些不同的事情,最后,它不能用于真正的基于 HTML5 的应用程序。
<rant>为什么,为什么,IE 团队永远不能生产出符合标准的浏览器?世界最流行的浏览器似乎注定要永远坐短途巴士,这真是一种可悲的耻辱。</rant>
此外,我发现除非您以 <!DOCTYPE html>
开始 HTML 文件,否则 IE9 将无法正确呈现内容,而 Firefox 和 Chrome 不需要 DOCTYPE
属性(我花了一段时间才弄清楚,顺便说一句)。不知道在这种情况下谁是对的,但 IE 的差异再次浪费了时间。
历史
- 2011 年 7 月 3 日:初始版本