使用 HTML5 canvas 绘制精美的图形
HTML5 是一个新兴标准,它正在以越来越快的速度替代久经考验的 HTML4。HTML5 是一个 W3C “工作草案” — 意味着它仍然处于开发阶段 — 它包含丰富的元素和属性,它们都支持现行的 HTML 4.01 版本规范。它还引入了几个新元素和属性,它们适用许多使用 web 页面的领域 — 音频、视频、图形、数据存储、内容呈现,等等。本文主要关注图形方面的增强:canvas。
新的 HTML5 canvas 是一个原生 HTML 绘图簿,用于 JavaScript 代码,不使用第三方工具。跨所有 web 浏览器的完整 HTML5 支持还没有完成,但在新兴的支持中,canvas 已经可以在几乎所有现代浏览器上良好运行了,但 Windows® Internet Explorer® 除外。幸运的是,一个解决方案已经出现,将 Internet Explorer 也包含进来。
本质上,canvas 元素是一个白板,直到您在它上面 “绘制” 一些可视内容。与拥有各种画笔的艺术家不同,您使用不同的方法在 canvas 上作画。您甚至可以在 canvas 上创建并操作动画,这不是使用画笔和油彩所能够实现的。
本 文探索新的 HTML canvas 元素,从简单地包含一个 canvas 元素到高级 JavaScript 交互(动画的关键)逐步进行演示。学习如何在一个 web 页面上显示 canvas。本文针对 web 设计师和开发人员,尽管 JavaScript 知识不是必须的,但理解该语言的运行方式将有所帮助。但是,HTML 知识是关键所在,尤其是如何创建一个基本 web 页面。
要查看本文展示的示例的实时实例,您需要一个浏览器并能访问 Internet。所有示例都在一个真实网站上提供(参见 参考资料)。
浏览器支持
确 定哪些浏览器支持 HTML5 和一个移动目标达到什么程度。在本文撰写之时,Google Chrome、Apple Safari 和 Mozilla Firefox 都支持大多数新的 HTML5 元素,它们都支持 canvas 元素。Internet Explorer 7 和 8 都不支持 HTML5;Internet Explorer 9 处于测试阶段,支持 HTML5,但还有一些问题。
在此期间,有一个针对不支持 HTML5 的 Internet Explorer 版本的补丁可用。这个补丁的前提是创建使用 JavaScript 代码的元素。例如,可以使用代码段 document.createElement('canvas')
创建一个可识别的 Canvas 标记;但是,这并不意味着有东西经过元素本身 。一个流行的解决方法是包含一个完整的基于 canvas 的 JavaScript 库,这个库由 Google 提供,称为 ExplorerCanvas— 或简称 excanvas
。下载并将其作为一个外部文件引用,如下所示。(参见 参考资料 中的链接,了解更多 excanvas
信息。)
<!--[if IE]> <script type="text/javascript" src="excanvas.js"></script> <![endif]-->
通过包含 excanvas
,您向 Internet Explorer 提供 canvas 及其大多数方法。
创建您的第一个 canvas
由于 canvas 只是一个 HTML 标记,因此它的显示方式只是包含在标记中。第一个示例(如 图 1 所示)显示一个最简单的 canvas。它之所以可见,是因为它通过 style
属性获得了一个颜色方案,通过 width
和 height
属性获得了其大小。
图 1. 一个空白 canvas
这个页面的代码简短明了,如 清单 1 所示。
清单 1. 包含一个 canvas 的 web 页面的 HTML
<!DOCTYPE html> <html> <head> <title>HTML5 Canvas Demo</title> <!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]--> </head> <body> <div style="margin-left:30px;"> <canvas id="myCanvasTag" width="400" height="400" style="background-color:blue;border: 10px yellow solid"></canvas> <br /><br /> <a href="index.html">back</a> </div> </body> </html>
这个 canvas 的宽度和高度均为 400 像素,边框为黄色,背景为蓝色。canvas 上没有任何实际绘图;绘图通过属于 canvas 的 JavaScript 方法完成。
canvas 方法
表 1 列示了几个附加到 canvas 上下文的方法。
表 1. canvas 方法
getContext(contextId) | 公开在 canvas 上绘图需要的 API。惟一(当前)可用的 contextID 是 2d 。 |
height | 设置 canvas 的高度。默认值是 150 像素。 |
width | 设置 canvas 的宽度。默认值是 300 像素。 |
createLinearGradient(x1,y1,x2,y2) | 创建一个线性渐变。起始坐标为 x1,y1 ,结束坐标为 x2,y2 。 |
createRadialGradient(x1,y1,r1,x2,y2,r2) | 创建一个放射状渐变。圆圈的起始坐标是 x1,y1 ,半径为 r1 。圆圈的结束坐标为 x2,y2 ,半径为 r2 。 |
addColorStop(offset, color) | 向一个渐变添加一个颜色停止。颜色停止(color stop) 是渐变中颜色更改发生的位置。offset 必须介于 0 到 1 之间。 |
fillStyle | 设置用于填充一个区域的颜色 — 例如,fillStyle='rgb(255,0,0)' . |
strokeStyle | 设置用于绘制一根直线的颜色 — 例如,fillStyle='rgb(255,0,0)' . |
fillRect(x,y,w,h) | 填充一个定位于 x 和 y ,宽度和高度分别为 w 和 h 的矩形。 |
strokeRect(x,y,w,h) | 绘制一个定位于 x 和 y ,宽度和高度分别为 w 和 h 的矩形的轮廓。 |
moveTo(x,y) | 将绘图位置移动到坐标 x,y 。 |
lineTo(x,y) | 从绘图方法结束的最后位置到 x,y 绘制一条直线。 |
构建更复杂的 canvas 应用程序
本小节将展示一系列示例,每个示例的功能都比前一个示例略有增加。
更深刻的视觉体验
首先,将一组矩形放置到 canvas 上。记住,矩形是拥有 4 条直边和 4 个直角的图形,正方形是矩形的变体。图 2 显示 canvas 上有一系列矩形,每个矩形都比前一个略小。注意,每个矩形的颜色都不同,使其更清晰明确。
图 2. 使用一些矩形填充的 canvas
清单 2 显示了用于创建图 2 中的 canvas 的代码。每个矩形都由两行代码创建:首先,fillStyle
方法使用颜色定义的红、绿、蓝(RGB)格式定义颜色(fillStyle='rgb(255,0,0)
)。然后,fillRect
方法(fillRect(50,50,300,300)
)定义大小。前两个值设置起始坐标,后两个值设置结束坐标。
清单 2. 使用 JavaScript 代码创建上下文并使用各种方法
<!DOCTYPE html> <html> <head> <title>HTML5 Canvas Demo</title> <!--[if IE]> <script type="text/javascript" src="excanvas.js"></script> <![endif]--> <script> window.onload=function() { var mycanvas=document.getElementById("myCanvasTag"); var mycontext=mycanvas.getContext('2d'); mycontext.fillStyle='rgb(0,0,255)'; mycontext.fillRect(0,0,400,400); mycontext.fillStyle='rgb(255,0,0)'; mycontext.fillRect(50,50,300,300); mycontext.fillStyle='rgb(0,255,0)'; mycontext.fillRect(100,100,200,200); mycontext.fillStyle='rgb(100,100,100)'; mycontext.fillRect(125,175,150,25); } </script> </head> <body> <div style="margin-left:30px;"> <canvas id="myCanvasTag" width="400" height="400" style="border: 10px yellow solid"> </canvas> <br /><br /> <a href="index.html">back</a> </div> </body> </html>
要在 canvas 上绘图,通过将 getContext('2d')
应用到 canvas 元素访问提供方法的 API。通过这个 document.getElementById
方法,canvas 元素被设置为一个 JavaScript 变量:
var mycanvas=document.getElementById("myCanvasTag");
然后将 getContext
应用到 canvas 元素,如下所示。
var mycontext=mycanvas.getContext('2d');
一旦一个变量被设置到上下文,所有方法就可以使用了。
下一个示例展示如何结合两种技术。一种是矩形重叠,方法是使用 fillRect
参数来定位矩形(见 图 3)。
图 3. 向一个 canvas 上的矩形应用透明度
第二种技术是 RGB 颜色处理的变体,即添加透明度。不使用 rgb
,而是使用 rgba
。a
表示 alpha 通道,该通道控制透明度。在图 3 中的示例中,第二个矩形的透明度被设置为 50%(或 .5),第三个设置为 25%(或 .25)。清单 3 显示了完整的标记。
清单 3. 使用透明度
<!DOCTYPE html> <html> <head> <title>HTML5 Canvas Demo</title> <!--[if IE]> <script type="text/javascript" src="excanvas.js"></script> <![endif]--> <script> window.onload=function() { var mycanvas=document.getElementById("myCanvasTag"); var mycontext=mycanvas.getContext('2d'); mycontext.fillStyle='rgb(0,0,255)'; mycontext.fillRect(30,30,300,300); mycontext.fillStyle='rgba(0,255,0,0.5)'; mycontext.fillRect(60,60,300,300); mycontext.fillStyle='rgba(255,0,0,0.25)'; mycontext.fillRect(90,90,300,300); } </script> </head> <body> <div style="margin-left:30px;"> <canvas id="myCanvasTag" width="400" height="400" style="border: 10px yellow solid"> </canvas> <br /><br /> <a href="index.html">back</a> </div> </body> </html>
渐变— 经过协调的颜色混合 — 原生于 canvas ,通过两种方法:createLinearGradient
和 createRadialGradient
。图 4 展示了一个线性渐变。addColorStop
方法定义一个颜色,以及它在渐变中变为活动状态的位置。由于一个渐变可以拥有多个颜色停止,因此这种定位是主观的。颜色定位值必须介于 0 到 1 之间,但测试变体和颜色停止的数量可以生成不同的结果,即使一个值(比如 .25)保持不变。换句话说,一个颜色停止可以将其位置设定为 .25,但是相关颜色可以从整个渐变路径的四分之一处之后一点开始发生,一直持续到渐变结束,具体情况取决于您设置其他颜色停止的位置。记住,这是一个新 的实现,可能还在改进过程中。渐变的一个好处是它们总是引人注目,无论代码和结果是否经过完美的协调。
图 4. 一个线性渐变
图 4 中的渐变通过 清单 4 中的 JavaScript 代码创建。
清单 4. 创建一个线性渐变
var mycanvas=document.getElementById("myCanvasTag"); var mycontext=mycanvas.getContext('2d'); var mygradient=mycontext.createLinearGradient(30,30,300,300); mygradient.addColorStop(0,"#FF0000"); mygradient.addColorStop(1,"#00FF00"); mycontext.fillStyle=mygradient; mycontext.fillRect(0,0,400,400);
注意,清单 4 中的颜色停止被全面实现为从这个方法本身创建的一个实时(on-the-fly)渐变的一个方法。语句 mygradient.addColorStop(0,"#FF0000")
显示颜色停止拥有两个参数:位置和颜色。
图 5 展示了一个放射状渐变。用于创建这个渐变的代码与清单 4 中的代码类似,除了使用 createRadialGradient
方法替代 createLinearGradient
方法之外。
图 5. 一个放射状渐变
用于创建图 5 中的放射状渐变的代码如 清单 5 所示。注意所有 5 个颜色停止。
清单 5. 创建一个放射状渐变
<!DOCTYPE html> <html> <head> <title>HTML5 Canvas Demo</title> <!--[if IE]> <script type="text/javascript" src="excanvas.js"></script> <![endif]--> <script> window.onload=function() { var mycanvas=document.getElementById("myCanvasTag"); var mycontext=mycanvas.getContext('2d'); var mygradient=mycontext.createRadialGradient(300,300,0,300,300,300); mygradient.addColorStop("0","magenta"); mygradient.addColorStop(".25","blue"); mygradient.addColorStop(".50","green"); mygradient.addColorStop(".75","yellow"); mygradient.addColorStop("1.0","red"); mycontext.fillStyle=mygradient; mycontext.fillRect(0,0,400,400); } </script> </head> <body> <div style="margin-left:30px;"> <canvas id="myCanvasTag" width="400" height="400" style="border: 10px blue solid"> </canvas> <br /><br /> <a href="index.html">back</a> </div> </body> </html>
多个 canvas
一个 web 页面能够包含多个 canvas,每个 canvas 引用它们自己的独特 JavaScript 上下文变量。结果,每个 canvas 都独立于其他 canvas 工作。图 6 展示了 4 个 canvas,每个 canvas 上的图像都不同。
图 6. 一个 web 页面上的多个 canvas
清单 6 显示了用于创建图 6 中的页面的代码。注意,每个 canvas 都有一个惟一 ID 且每个上下文都是惟一的。
清单 6. 一个 web 页面上的多个 canvas
<!DOCTYPE html> <html> <head> <title>HTML5 Canvas Demo</title> <!--[if IE]> <script type="text/javascript" src="excanvas.js"></script> <![endif]--> <script> window.onload=function() { var mycontext1=document.getElementById("myCanvasTag1").getContext('2d'); var mycontext2=document.getElementById("myCanvasTag2").getContext('2d'); var mycontext3=document.getElementById("myCanvasTag3").getContext('2d'); var mycontext4=document.getElementById("myCanvasTag4").getContext('2d'); // gradient 1 var mygradient1=mycontext1.createLinearGradient(30,30,90,90); mygradient1.addColorStop(0,"#FF0000"); mygradient1.addColorStop(1,"#00FF00"); mycontext1.fillStyle=mygradient1; mycontext1.fillRect(0,0,100,100); // gradient 2 var mygradient2=mycontext2.createLinearGradient(30,30,90,90); mygradient2.addColorStop(1,"#FF0000"); mygradient2.addColorStop(0,"#00FF00"); mycontext2.fillStyle=mygradient2; mycontext2.fillRect(0,0,100,100); var mygradient3=mycontext3.createLinearGradient(30,30,90,90); mygradient3.addColorStop(0,"#0000FF"); mygradient3.addColorStop(.5,"#00FFDD"); mycontext3.fillStyle=mygradient3; mycontext3.fillRect(0,0,100,100); var mygradient4=mycontext1.createLinearGradient(30,30,90,90); mygradient4.addColorStop(0,"#DD33CC"); mygradient4.addColorStop(1,"#EEEEEE"); mycontext4.fillStyle=mygradient4; mycontext4.fillRect(0,0,100,100); } </script> </head> <body> <div style="margin-left:30px;"> <canvas id="myCanvasTag1" width="100" height="100" style="border: 10px blue solid"> </canvas> <canvas id="myCanvasTag2" width="100" height="100" style="border: 10px green solid"> </canvas> <br /> <canvas id="myCanvasTag3" width="100" height="100" style="border: 10px red solid"> </canvas> <canvas id="myCanvasTag4" width="100" height="100" style="border: 10px black solid"> </canvas> <br /><br /> <a href="index.html">back</a> </div> </body> </html>
JavaScript 事件和动画
本 文已经展示了可用于 canvas 的各种方法,每种方法都创建了一个可视结果。现在我们提高一个等级,看看 canvas 如何实现事件和动画。JavaScript 能够识别许多事件,包括在一个特定 web 页面元素上方移动或悬停鼠标。JavaScript 语言能够识别更多事件,下面的示例将使用其中几个。
整合事件
图 7 中的示例是使用前面清单中的方法创建的。现在我们添加几个新技术。面部的鼻子、眼睛和圆形项目以及外部边界都使用 arc
方法创建。眼睫毛被绘制为线条,嘴被创建为一条贝赛尔曲线。图 7 还在 canvas 底部显示了一些文本,这是使用 fillText
方法创建的。
图 7. 使用 JavaScript 事件绘制一张正在眨眼的脸
清单 7 显示图 7 使用的代码。
清单 7. 使用事件创建一个眨眼
<!DOCTYPE html> <html> <head> <title>HTML5 Canvas Demo</title> <!--[if IE]> <script type="text/javascript" src="excanvas.js"></script> <![endif]--> <script> window.onload=function() { var mycanvas=document.getElementById("myCanvasTag"); var mycontext=mycanvas.getContext('2d'); //draw face mycontext.beginPath(); mycontext.arc(300, 250, 200, 0, Math.PI * 2, true); mycontext.closePath(); mycontext.stroke(); //draw left eye mycontext.beginPath(); mycontext.arc(220, 150, 30, 0, Math.PI * 2, true); mycontext.closePath(); mycontext.fillStyle='rgb(100,100,225)'; mycontext.fill(); //draw left iris mycontext.beginPath(); mycontext.arc(220, 150, 10, 0, Math.PI * 2, true); mycontext.closePath(); mycontext.fillStyle='rgb(0,0,0)'; mycontext.fill(); //draw left eyelid mycontext.beginPath(); mycontext.arc(220, 150, 30, 0, Math.PI, true); mycontext.closePath(); mycontext.fillStyle='rgb(200,200,200)'; mycontext.fill(); //draw left eyelashes mycontext.strokeStyle='rgb(0,0,0)'; lashes(mycontext,198, 170, 193, 185); lashes(mycontext,208, 177, 204, 193); lashes(mycontext,220, 180, 220, 195); lashes(mycontext,232, 177, 236, 193); lashes(mycontext,242, 170, 247, 185); mycontext.stroke(); openeye(); //draw right eyelashes mycontext.strokeStyle='rgb(0,0,0)'; lashes(mycontext, 358, 170, 353, 185); lashes(mycontext, 368, 177, 364, 193); lashes(mycontext, 380, 180, 380, 195); lashes(mycontext, 392, 177, 396, 193); lashes(mycontext, 402, 170, 407, 185); mycontext.stroke(); //draw nose mycontext.beginPath(); mycontext.arc(300, 250, 20, 0, Math.PI * 2, true); mycontext.closePath(); mycontext.stroke(); // draw smile mycontext.beginPath(); mycontext.lineWidth = 10; mycontext.moveTo(180, 320); mycontext.bezierCurveTo(140, 320, 340, 420, 400, 360); mycontext.closePath(); mycontext.stroke(); //draw message at bottom mycontext.font="1em sans-serif"; mycontext.fillStyle="rgb(0,0,0)"; mycontext.fillText("Move the mouse over and off the canvas - the face winks!", 10, 480); } function lashes(cntx,x1,y1,x2,y2) { cntx.moveTo(x1,y1); cntx.lineTo(x2,y2); } function closeeye() { //close right eye var mycanvas=document.getElementById("myCanvasTag"); var mycontext=mycanvas.getContext('2d'); mycontext.beginPath(); mycontext.arc(380, 150, 30, 0, Math.PI * 2, true); mycontext.closePath(); mycontext.fillStyle='rgb(200,200,200)'; mycontext.fill(); } function openeye() { //open right eye var mycanvas=document.getElementById("myCanvasTag"); var mycontext=mycanvas.getContext('2d'); mycontext.beginPath(); mycontext.arc(380, 150, 30, 0, Math.PI * 2, true); mycontext.closePath(); mycontext.fillStyle='rgb(100,100,225)'; mycontext.fill(); //draw right iris mycontext.beginPath(); mycontext.arc(380, 150, 10, 0, Math.PI * 2, true); mycontext.closePath(); mycontext.fillStyle='rgb(0,0,0)'; mycontext.fill(); //draw right eyelid mycontext.beginPath(); mycontext.arc(380, 150, 30, 0, Math.PI, true); mycontext.closePath(); mycontext.fillStyle='rgb(200,200,200)'; mycontext.fill(); } </script> </head> <body> <div style="margin-left:30px;"> <canvas id="myCanvasTag" width="600" height="500" style="border: 5px blue solid" onmouseover="closeeye()" onmouseout="openeye()"></canvas> <br /><br /> <a href="index.html">back</a> </div> </body> </html>
图 7 中的脸通过一些 JavaScript 事件而改变。特别是, onmouseover
和 onmouseout
事件分别用于调用 closeeye()
和 openeye()
函数。这些函数不是 canvas 的方法,但是标准 JavaScript 函数。事件和函数之间的连接在 canvas 元素本身中进行。在 清单 7 中,页面的主体区域接近代码的底部,那是 canvas 所在的位置。canvas 标记内是:
onmouseover="closeeye()" onmouseout="openeye()"
清单 7 中出现了一个新的构造 —beginPath()
和 endPath()
的使用,它们用于明确区分独立的复杂绘图动作。封装在其中的代码绘制一个特殊的图像,以分隔其他绘图动作。
关于 清单 7 中的代码的几个值得注意的地方列示如下:
- 当页面打开时,通过调用
openeye()
函数来绘制右眼。 - 使用
fillText
方法将文本写到 canvas 上。 - 在
arc
方法中,MATH.PI * 2
创建了一个完整的圆圈,而MATH.PI
将只创建一个半圆(例如,眼皮)。
动画
JavaScript 打包了一个强大的编程利器。这种语言能够执行很多操控,如 清单 8 所示。这个代码重复运行,在 canvas 上绘制一些线条。线条颜色随机设置。
清单 8. 使用 JavaScript 创建动画
<!DOCTYPE html> <html> <head> <title>HTML5 Canvas Demo</title> <!--[if IE]> <script type="text/javascript" src="excanvas.js"></script> <![endif]--> </head> <body> <div style="margin-left:30px;"> <canvas id="myCanvasTag" width="400" height="400" style="border: 10px blue solid"> </canvas> <br /><br /> <a href="index.html">back</a> </div> <script> var mycanvas = document.getElementById("myCanvasTag") var mycontext = mycanvas.getContext('2d'); var x; var y; var x2; var y2; var r; var g; var b; function line() { x=Math.floor(Math.random()*190) + Math.floor(Math.random()*190); y=Math.floor(Math.random()*190) + Math.floor(Math.random()*190); x2=Math.floor(Math.random()*190) + Math.floor(Math.random()*190); y2=Math.floor(Math.random()*190) + Math.floor(Math.random()*190); r=Math.floor(Math.random()*255); g=Math.floor(Math.random()*255); b=Math.floor(Math.random()*255); mycontext.moveTo(x, y); mycontext.lineTo(x2, y2); mycontext.strokeStyle='rgb(' + r + ',' + g + ',' + b + ')'; mycontext.lineWidth=Math.floor(Math.random()*6); mycontext.stroke(); mycontext.restore(); } setInterval(line, 100); </script> </body> </html>
图 8 显示了这个动画的一张快照。清单 8 中的代码与本文其他所有代码示例都不同,因为 JavaScript 块被放置到页面底部,canvas 元素下方。这确保 canvas 在代码运行之前就已经被呈现了。
图 8. JavaScript 用于绘制无穷无尽的随机线条
结束语
HTML5 被定位于用于改变 web 开发的面貌。新的元素使得页面布局更简单,支持通过浏览器存储本地数据,拥有 canvas 之类的音频、视频和图形平台。随着浏览器升级以支持更多新功能,web 的性质和用户体验将变得妙趣横生。web 开发前景一片光明!
参考资料
学习
- 找到本文中的示例的 实时演示 。
- WHATWG 是一个开发人员社区,协助 W3C 改进 HTML5。
- 马上 开始实现 HTML5。
- 阅读来自 Mozilla 开发人员的 canvas 教程和演示。
- PHPGuru.org 提供关于 canvas 用法的信息,以及许多很棒的演示和示例。
- developerWorks Web development 专区:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。
- developerWorks Ajax 资源中心:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。
- developerWorks Web 2.0 资源中心,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过 Web 2.0 新手入门 栏目,迅速了解 Web 2.0 的相关概念。
- 随时关注 developerWorks 技术活动和网络广播。
- 观看 developerWorks 演示中心,包括面向初学者的产品安装和设置演示,以及为经验丰富的开发人员提供的高级功能。
- 查看 HTML5 专题,了解更多和 HTML5 相关的知识和动向。
获得产品和技术
- 下载 用于 Internet Explorer 的 excanvas.js 库。
- 下载 IBM 产品评估试用版软件 或 IBM SOA 人员沙箱,并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
- 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
- 关注 developerWorks 成员 关于 web 主题的共享书签。
- 快速找到答案:访问 Web 2.0 Apps 论坛。
- 快速找到答案:访问 Ajax 论坛。
相关推荐
表格的现在还是较为常用的一种标签,但不是用来布局,常见处理、显示表格式数据。在HTML网页中,要想创建表格,就需要使用表格相关的标签。<table> <tr> <td>单元格内的文字</td> ...