65.9K
CodeProject 正在变化。 阅读更多。
Home

HTML5 Canvas 自绘持久背景

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.80/5 (4投票s)

2011年7月5日

CPOL

4分钟阅读

viewsIcon

39431

downloadIcon

548

如何让 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* - 实现 PointRect 类,以辅助绘图。

最值得注意的是 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 日:初始版本
© . All rights reserved.