[tr][td]简介: 新的 HTML5 规范旨在帮助开发人员更轻松的编写出各类 Web 应用,以顺应当前 SaaS,云计算以及 RIA等技术的最新趋势。在 HTML5 得以广泛推广之前,开发人员通常使用 SVG,VML 等技术进行 Web 绘图操作,但这些基于 XML的绘图语言声明式的绘图方式并不能满足复杂绘图操作在性能上的需求,比如 Web 游戏所需要的像素级别的绘图能力。HTML5 canvas元素的出现填补了这种不足,开发人员可以使用 JavaScript 脚本语言在 canvas 中进行一系列基于命令的图形绘制操作,本文将通过讲解如何使用canvas 元素进行基本绘图操作,以及完成简单的动画和用户交互任务,阐明 canvas 在帮助构建 Web 图形类应用时所能够提供的能力。
背景介绍 HTML5 中新引入的 canvas 元素使得 Web 开发人员在无须借助任何第三方插件(如 Flash,Silverlight)的情况下,可以直接使用JavaScript 脚本在 Web 页面进行绘图。它首次由苹果公司的 Webkit 框架引入实现,并成功运用在 Safari 浏览器中,读者在 这里可以体验到基于 canvas 的精彩示例。目前,canvas 已成为HTML5 规范中的事实性标准,并且已经被 Firefox 3.0+, Safari 3.0+, Chrome 3.0+, Opera10.0+等浏览器所支持。最近(本文撰写之时),IE 也正式宣称将在其 9.0 版本之后,开始对 canvas 元素进行支持。 基于 canvas 的绘图填补了 SVG 绘图的在复杂绘图操作,特别是性能方面的不足,可广泛应用于 Dashboard,2D/3D Game 等 Web应用中。 基本绘图API 在了解了什么是 canvas 元素之后,是时候使用 canvas 在 Web 页面上真正进行的绘图操作了。实际上,单独的一个 canvas标记只是在页面中定义了一块矩形区域,并无特别之处,开发人员只有配合使用 JavaScript 脚本,才能够完成各种图形,线条,以及复杂的图形变换操作,与基于SVG 来实现同样绘图效果来比较,canvas 绘图是一种像素级别的位图绘图技术,而 SVG则是一种矢量绘图技术。正鉴于这种本质机理的不同,如何更快速高效的进行 canvas 渲染成为各主流 JavaScript执行引擎性能比拼的重要指标之一。目前,Chrome 的 V8, Firefox 的 SpiderMonkey 以及 Safari 的 Nitro等引擎都已经能够很好的满足二维绘图所需的必要性能指标,虽然在运行一些基于 canvas 的游戏时 CPU 占用率还是相对较高,但我们有理由相信随着 NVIDIA和 AMD 等一系列硬件厂商的参与,硬件加速技术将大大提升 Web 应用的性能。 在开始绘图之前,我们需要首先创建一个指定大小的 canvas,并为其指定一个 id,方便在 JavaScript 脚本中获取该 DOM 实例对象。声明一个canvas 节点的方式如下所示。 [table=98%][tr][td] Fallback content, in case the browser does not support Canvas. [/td][/tr][/table] 需要指明的是,由于无法保证所有用户使用的浏览器都能够支持 canvas 元素,所以在目前开发基于 canvas 的 Web应用中需要增加“Fallback content”,以提示用户他们无法正常体验此功能的原因或建议他们去下载最新的浏览器。 这里,好奇的读者可能会问,既然这是一个普通的 DOM节点,那么便意味着可以通过直接改变其 width 或 height 属性值来改变 canvas 的大小?确实如此,但是,正如之前提到的 canvas是一种像素级别的绘图方法,因而,一旦动态调整 canvas 的大小,canvas 将被“重置”到一个新的初始状态,即便是如下这种操作,也会将 canvas内的位图清除并将所有相关属性恢复到初始值的状态。当然,我们也可以把这当作重置 canvas 的小技巧来使用。 [table=98%][tr][td] document.getElementById("canvas").width = document.getElementById("canvas").width;[/td][/tr][/table] 简单图形绘制 基于 canvas 的绘图并不是直接在 canvas 标记所创建的绘图画面上进行各种绘图操作,而是依赖画面所提供的 渲染上下文(RenderingContext),所有的绘图命令和属性都定义在渲染上下文当中。在通过 canvas id 获取相应的 DOM对象之后首先要做的事情就是获取渲染上下文对象。 渲染上下文与 canvas 一一对应,无论对同一 canvas 对象调用几次 getContext()方法,都将返回同一个上下文对象。目前,所有支持 canvas 标签的浏览器都支持 2D 渲染上下文,可以使用如下的代码来获取该对象。 [table=98%][tr][td] var context = document.getElementById("canvas").getContext("2d");[/td][/tr][/table] 除此之外,在不久的将来,开发人员还会能够得到基于 OpenGL 的 3D 渲染上下文以在 canvas 中进行 3D 绘图。 与 SVG 不同,canvas 原生支持的基本图形只有矩形一种,至于其他的圆形,多边形等图形则都由路径来负责绘制实现。清单 1展示了如何使用渲染上下文中的矩形绘图方法完成了图 1 所示图形。 图 1. 清单 1对应的示例图形 清单 1. 绘制 canvas 矩形 [table=98%][tr][td] function drawRect(){ var canvas = document.getElementById('canvas'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); // 获取 2D 渲染上下文 ctx.clearRect(0,0,300,200) ;// 清除以(0,0)为左上坐标原点,300*200 矩形区域内所有像素 ctx.fillStyle = '#00f'; // 设置矩形的填充属性,#00f 代表蓝色 ctx.strokeStyle = '#f00'; // 设置矩形的线条颜色,#f00 代表红色 ctx.fillRect(50,25,150,80); // 使用 fillStyle 填充一个 150*80 大小的矩形 ctx.strokeRect(45,20, 160, 90); // 以 strokeStype 属性为边的颜色绘制一个无填充矩形 } }[/td][/tr][/table] 绘制路径 在开始动手绘制路径之前,首先需要明确的是:矩形绘制 API 是一种即时性的API,他会在相应的绘图函数执行完毕之后,将图形即时的渲染在画面上。然而路径绘制 API 并非如此,完整的路径绘制过程大致可以分为如下两个阶段: [*]定义路径轮廓: [*]绘制路径 清单 2 绘制一个图 2 所示半圆弧,并通过 closePath() 方法完成图形的闭合。 图 2. 清单2 对应的示例图形 清单 2. 绘制 canvas 路径 [table=98%][tr][td] function draw(){ var canvas = document.getElementById('canvas'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); ctx.fillStyle = '#00f'; ctx.strokeStyle = '#f00'; ctx.beginPath(); ctx.arc(75,75,30,0,Math.PI, false); // 绘制一条半圆弧线 ctx.closePath(); // 自动绘制一条直线来关闭弧线。若不调用此方法,将仅仅显示一条半圆弧 ctx.fill(); // 可以尝试注释掉 fill 或者 stroke 函数,观察图形的变化 ctx.stroke(); } }[/td][/tr][/table] 二维变形 Canvas 绘图中另一个重要的概念是 绘画状态(DrawingState),绘画状态反映了渲染上下文当前的瞬时状态,开发人员可以通过对绘画状态的保存 /恢复操作而快速的回到之前使用的各种属性和变形操作。绘画状态主要由以下三个部分构成: [*]当前的变形矩阵(transformation matrix) [*]当前的裁剪区域(clipping region) [*]当前上下文中的属性,比如 strokeStyle, fillType, globalAlpha, font 等等。 开发人员可以使用 save 和 restore 两种方法来保存和恢复 canvas 状态,每调用 save 方法,都会将当前状态压入堆栈中,而相应的restore 方法则会从堆栈中弹出一个状态,并将当前画面恢复至该状态。绘画状态在 canvas 图形变形操作中应用极为广泛,也非常重要,因为调用一个restore 方法远比手动恢复先前状态要简单许多,因而,一个较好的习惯是在做变形操作之前先保存 canvas 状态。 二维绘图的常用变形操作在 canvas中都可到了很好的支持,包括平移(Translate),旋转(Rotate),伸缩(Scale)等等。由于所有的变形操作都基于变形矩阵,因而开发人员始终需要记住一点的就是,一旦没有使用save/restore 操作保持住原来的绘图状态,那么后续的绘图操作,都会在当前所应用的变形状态下完成。清单 3使用平移和旋转方法绘制了如下所示画面。 图 3. 清单 3 所示示例图形 清单 3. 使用平移 / 旋转变形方法绘制复杂位图 [table=98%][tr][td] function drawPointCircle(){ var canvas = document.getElementById('canvas'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); ctx.translate(150,150); // 将 canvas 的原点从 (0,0) 平移至(150,150) for (i=1;i |
|