在开发网页应用时,经常需要绘制动态更新的图表。这些图表不仅需要在页面加载时自动计算其大小,还要包含标题和缩放的网格,并显示X和Y轴的图例。最重要的是,图表的数据需要每秒更新多次。由于动态尺寸的原因,不能使用现成的背景图像;希望画布在页面加载时自己绘制背景。为了效率,不想在每次更新图表线条时都重绘背景(图例和网格)。显然,这需要两层,一层用于背景,另一层用于数据。不幸的是,画布不支持层,所以没有显而易见的简单答案。
探索了一些建议,比如使用多个“堆叠”的画布元素,使用绝对元素定位和z-index,但即使这样,对于正在做的事情来说也过于复杂。然后发现了一个名为canvas.toDataURL()
的方法,有了想法:为什么不在页面加载时让画布动态绘制其背景,然后通过toDataURL()
获取该图像,并将该图像作为画布元素的背景图像呢?事实证明,这效果很好;本文介绍了解决方案。
附带的示例是一个简单的实现技术。(可以解压缩示例,并将浏览器指向index.html
,它将在没有web服务器的情况下运行)。
以下是附带示例的(非动画)截图:图表的文本和网格线是背景图像的一部分;红色球体定期重绘,以在网格边界内弹跳。
需要基本了解HTML/CSS/JavaScript,并且最好有HTML5画布的使用经验。此外,需要一个支持HTML5画布的浏览器,如Firefox、IE9或Chrome。
如上所述,该技术的关键在于canvas.toDataURL()
:
var canvas = document.getElementById("some-canvas-id");
var context = canvas.getContext("2d");
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
// 更多背景内容...
var base64 = canvas.toDataURL();
canvas.style.backgroundImage = "url(" + base64 + ")";
现在可以反复清除和重绘画布,而不会丢失背景。
附带的示例由4个文件组成:
index.html
- 声明画布元素,在onload()
中设置,然后运行一些简单的JavaScript定时器来动画球体。canvas.js
- 实现“Chart”类,它封装了所有画布的背景声明和绘制。canvashelpers.js
- 实现绘制HTML5画布上“清晰”线条的辅助函数(有关详细信息,请参见下面的“要点”)。primitives.js
- 实现Point
和Rect
类,这些类有助于绘制。最值得注意的是Chart.createBackground()
和Chart.clear()
(都在chart.js
中):
createBackground()
绘制自定义背景。这当然会根据应用程序的不同而有所不同,但它概述了一些应该对大多数用途都通用的基本思想。clear()
是一个非常简单的小技巧,对动画代码非常有用。它表明画布的上下文会在设置画布的高度时被清除(擦除),即使它被设置为当前值也是如此。虽然HTML5画布以“通常的方式”绘制线条,通过moveto()
和lineto()
,但它处理这些线条的抗锯齿是从未遇到过的。通常,如果做了例如moveto(10,10); lineto(100,10);
,会得到一个清晰的1像素高的横线。但是使用画布,这将导致一个“模糊的”(在2个像素上抗锯齿)横线...这完全不是预期的。事实证明,画布使用浮点(而不是int)地址空间,为了获得预期的清晰1px线,必须使用半像素偏移,例如moveto(10.5, 10.5); lineto(100.5, 10.5);
。
花了一些时间来理解发生了什么;这个页面解释得很好。示例代码包括辅助函数crispLine()
和crispRect()
(在canvashelpers.js
中),这些函数自动化处理这种半像素偏移,以产生预期的清晰线条。
在这个项目的过程中,做了很多关于HTML5的许多特性(不仅仅是画布)的实验,发现Chrome的支持是最好的。Chrome很好地(且高效地)支持一切,Firefox支持大多数东西(尽管不是全部,并且是内存猪),而IE9仍然是...IE:它支持一些东西,不支持其他东西,做一些事情不同,最后,它不适用于真正的基于HTML5的应用。