使用 HTML5 Canvas 进行数据可视化

下载代码示例:http://archive.msdn.microsoft.com/mag201201HTML5

在网络时代的早期阶段,Web 只不过是静态文本和链接的集合,人们越来越关注为其他类型的内容提供支持。 1993 年,Mosaic 浏览器(后来发展为 Netscape Navigator)的创建者 Marc Andreessen 提出将 IMG 标记作为在页面上的文本中嵌入图像的标准。 此后不久,IMG 标记成为向网页中添加图形资源的事实上的标准—至今仍在使用这一标准。 您甚至可以说,由于 Web 应用已经从 Web 文档发展到 Web 应用程序,因此 IMG 标记比以往更加重要。

一般而言,媒体肯定比以往更加重要,尽管 Web 上的媒体需求在过去 18 年中不断演变,但图像一直是静态的。 Web 作者越来越希望能够在其网站和应用程序中使用动态媒体(例如音频、视频和交互式动画),直到最近,主要的解决方案仍然是 Flash 或 Silverlight 之类的插件。

现在有了 HTML5,浏览器中的媒体元素大受青睐。 您可能听说过新的 Audio 和 Video 标记,二者均允许这些类型的内容充当浏览器中的第一类对象,不需要任何插件。 下个月的文章将深入介绍这两个元素及其 API。 您可能还听说过 canvas 元素,它是一个绘图表面,包含一组丰富的 JavaScript API,这些 API 使您能够动态创建和操作图像及动画。 IMG 对静态图形内容起到了哪些作用,canvas 就可能对可编写脚本的动态内容起到哪些作用。

尽管 canvas 元素令人兴奋,但它也面临一些认知问题。 由于画布功能强大,它通常通过复杂动画或游戏来展示,尽管这些动画或游戏确实可以传达能够实现的功能,但它们也可能让您产生误解,认为画布使用起来非常复杂和困难,只应该尝试将其用于复杂情况(如动画或游戏)。

在本月的文章中,我将撇开画布华而不实的功能和复杂性,介绍它的一些简单的基本用法,目标是将画布定位为在 Web 应用程序中进行数据可视化的一个功能强大的选项。 明确这一点后,我将重点讨论您如何开始使用画布,以及如何绘制简单的线条、形状和文本。 然后,我将讨论您如何在形状中使用渐变,以及如何向画布中添加外部图像。 最后,按照我在本系列中的一贯做法,我会简要探讨一下对较早版本浏览器的“填充代码”画布支持。

HTML5 Canvas 简介

根据 W3C HTML5 规范 (w3.org/TR/html5/the-canvas-element.html),canvas 元素“为脚本提供取决于分辨率的位图画布,该画布可用于动态呈现图形、游戏图形或其他可视图像。”Canvas 实际是在两个 W3C 规范中定义的。 第一个规范是 HTML5 核心规范的一部分,其中详细定义了该元素本身。 此规范介绍如何使用 canvas 元素、如何获取其绘图上下文、用于导出画布内容的 API 以及浏览器供应商的安全注意事项。 第二个规范是 HTML Canvas 2D Context (w3.org/TR/2dcontext),我稍后再介绍该规范。

开始使用画布非常简单,只需在 HTML5 标记中添加 <canvas> 元素即可,如下所示:

              <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <title>My Canvas Demo </title>
        <link rel="stylesheet" href="style.css" />
      </head>
      <body>
        <canvas id="chart" width="600" height="450"></canvas>
      </body>
    </html>
            

尽管现在我已经在 DOM 中包含 canvas 元素,但在页面上放置此标记不会产生任何效果,因为在您添加内容之前,canvas 元素中没有任何内容。 这就是绘图上下文应运而生的原因。 为了说明我的空白画布所在的位置,我可以使用 CSS 设置其样式,因此我将在空白元素周围添加一条蓝色虚线。

          canvas {
    border-width: 5px;
    border-style: dashed;
    border-color: rgba(20, 126, 239, 0.50)
}
       

在 Internet Explorer 9+、Chrome、Firefox、Opera 或 Safari 中打开我的页面时的结果如图 1 所示。

图 1 已设置样式的空白 Canvas 元素

使用画布时,您将在 JavaScript 中执行大多数工作,可通过 JavaScript 利用画布绘图上下文公开的 API 来操作图面的每个像素。 要获取画布绘图上下文,您需要从 DOM 获得您的 canvas 元素,然后调用该元素的 getContext 方法。

              var _canvas = document.getElementById('chart');
    var _ctx = _canvas.getContext("2d");
            

GetContext 返回一个对象,其中包含可用于在相关画布上绘图的 API。 该方法的第一个参数(在本例中为“2d”)指定我们要用于画布的绘图 API。 “2d”指的是我之前提到的 HTML Canvas 2D Context。 您可能已经猜到,2D 表示这是一个二维绘图上下文。 到撰写本文时为止,2D Context 是唯一受到广泛支持的绘图上下文,我们将在本文中使用该上下文。 围绕 3D 绘图上下文的工作和试验正在进行当中,因此将来画布应该能够为我们的应用程序提供更多功能。

绘制线条、形状和文本

现在页面上已经有了 canvas 元素,并且我们已经在 JavaScript 中获取了其绘图上下文,我们可以开始添加内容了。 因为我想重点介绍数据可视化,所以我将使用画布绘制一个条形图来表示一个虚构的体育用品商店当月的销售数据。 本练习将需要为轴绘制轴线;为条绘制形状和填充;以及为每个轴和条上的标签绘制文本。

让我们从 x 和 y 轴的轴线开始。 在画布上下文中绘制直线(或路径)分两个步骤进行。 首先,使用一系列 lineTo(x, y) 和 moveTo(x, y) 调用在图面上“描摹”直线。 每种方法都会获取画布对象(从左上角开始)上的 x 坐标和 y 坐标(而非屏幕本身上的坐标)以便在执行操作时使用。 moveTo 方法将移至所指定的坐标,lineTo 将在当前坐标与您指定的坐标之间描摹一条直线。 例如,以下代码将在图面上描摹我们的 y 轴:

              // Draw y axis.
              _ctx.moveTo(110, 5);
    _ctx.lineTo(110, 375);
            

如果您向脚本中添加此代码并在浏览器中运行它,您会注意到什么也不会发生。 因为这第一步只是一个描摹步骤,并未在屏幕上绘制任何内容。 描摹仅指示浏览器记录将在以后某个时刻刷新到屏幕上的路径操作。 当我准备好在屏幕上绘制路径时,我可以选择设置我的上下文的 strokeStyle 属性,然后调用 stroke 方法,该方法将填充不可见线条。 结果如图 2 所示。

              // Define Style and stroke lines.
              _ctx.strokeStyle = "#000";
    _ctx.stroke();
            

图 2 画布上的一条直线

因为定义线条(lineTo、moveTo)和绘制线条 (stroke) 是相对独立的,所以您实际可以批量处理一系列 lineTo 和 moveTo 操作,然后将它们同时输出到屏幕上。 我将通过此方法绘制 x 轴和 y 轴并完成在每个轴的一端绘制箭头的操作。 用于绘制轴的完整函数如图 3 所示,结果如图 4 所示。