HTML5 基础知识,第 4 部分: 最后的完善

Canvas 元素

HTML5 反映出了如今通过网络和云计算进行商业活动的方式发生的里程碑式变化。本文是关注 HTML5 变化的由 5 篇文章组成的系列文章中的第 4 篇,介绍了 HTML5 Canvas 元素,并使用几个例子来演示它的功能。

Grace Walker, IT 顾问, Walker Automated Services

Grace Walker,伊利诺斯州芝加哥市 Walker Automated Services 的合伙人,是一个有丰富背景和广博经验的IT顾问。她曾在 IT 界包括电信、教育、金融服务以及软件等行业工作过,曾经担任过经理、行政人员、程序员、技术指导、业务分析师、产品分析师以及系统分析师等职务。



2012 年 7 月 18 日

HTML5 作者集设计者和开发者于一身,他们的主要任务就是构建高效的丰富 Internet 应用程序 (RIA),尤其是富 UI。我所说的高效 是指进行系统的、全面的改进,以数字的方式为站点所有者、所有者代理和站点使用者之间的对话提供便利。

RIA 是满足用户体验的本源和工具,因此也是任何以网络为中心的成功企业的关键组成部分。实质上,以网络为中心的活动在某种程度上是协作性的活动。当机构想在各个级别(包括市场营销和管理)上取得成功时,实现数字协作的成功方法至关重要。站点的成功主要取决于站点满足访问用户的质量要求的效率。

如您所见,HTML5 是专门为实现具有跨平台功能、融合电信、统一语言、广泛计算和开源系统的协作式 “网络世界” 而量身定制的。本系列的前 3 篇文章主要关注语义、适合的编码方法、输入在关键转换过程中所扮演的角色以及站点管理的最佳实践,所有这些都是为创建有组织的、符合逻辑的 RIA 打下基础。贯穿每篇文章共同线索是丰富用户体验的创建和管理对于实现站点所有者的代理目标的重要性。HTML5 Canvas 在开发高效 RIA 的过程中扮演重要角色。

常用缩略语

  • 2D:二维
  • Ajax:异步 JavaScript + XML
  • API:应用编程接口
  • HTML:超文本标记语言
  • HTML5:HTML 版本 5
  • UI:用户界面

什么是 Canvas?

HTML5 Canvas 是一个极其有用的绘制和动画元素。Canvas 使用 JavaScript 直接在页面上绘制图形。Canvas 能够定义和控制矩形区域,并允许以动态方式或通过脚本呈现 2D 形状和位图。

对于生成能够增强 UI、图表、相册、图形、动画和嵌入式绘制应用程序的出色视觉材料而言,HTML5 Canvas 非常完美。Canvas 元素可以通过几种方法来绘制路径、矩形、圆形和字符。


Canvas 坐标

在画布上进行绘制的前提条件是熟悉网格和坐标空间。空间区域的长度和宽度的单位为像素。用户可以围绕 xy 坐标构建画布。当坐标为 x=0, y=0 时,画布位于左上角。

矩形画布区域的默认宽度为 300 像素,高度为 150 像素,但您可以通过修改这两个属性来获得所需的画布大小。图 1 显示了如何实现 xy 坐标。

图 1. 画布坐标
通过正方形的对角线和水平线显示每个交叉点的坐标

图 1 显示了长宽都为 100 像素的画布区域:

  • 左上角的坐标为 x=0, y=0
  • x 值在水平方向增加,而 y 值在垂直方向增加。
  • 右下角的坐标为 x=100, y=100
  • 中心点的坐标为 x=50, y=50

开始

在向画布添加内容之前,必须先在 HTML 文件中定义画布,因此必须创建 JavaScript 代码,用它来访问 <canvas> 标记并与 HTML5 Canvas API 通信,这样就可以绘制图形。

<canvas> 标记的基本结构为:

<canvas id="myCanvas" width="200" height="200"></canvas>

canvas 元素有两个专有的属性:widthheight。此外,Canvas 还拥有所有关键的 HTML5 属性,比如 classidname。在上面显示的代码中就用到了 id 属性。JavaScript 使用这里创建的画布 id 来识别要绘制内容的画布。JavaScript 使用 document.getElementById() 方法来确定正确的画布,如下所示:

var canvas = document.getElementById("myCanvas");

每个画布都必须有一个上下文定义,如下所示。到目前为止,官方的规范仅识别 2D 环境:

var context = canvas.getContext("2d");

在识别画布并指定其上下文之后,就可以在其上绘制内容了。


绘制工具、效果和变形

这里讨论 HTML5 Canvas 将用到的各种绘制工具、效果和变形。这些绘制工具包括:

  • 线条
  • 矩形
  • 弧形
  • 贝塞尔曲线和二次方曲线
  • 圆形和半圆形

将使用的画布效果包括:

  • 填充和笔触
  • 线性渐变和辐射渐变

要讨论的变形包括:

  • 缩放
  • 旋转
  • 转换

绘制线条

要在画布上绘制线条,可以使用 moveTo()lineTo()stroke() 方法。此外,还可以使用 beginPath() 方法来重新设置当前的路径:

  • context.beginPath();
  • Context.moveTo(x,y);
  • Context.lineTo(x,y);
  • Context.stroke(x,y);

beginPath() 方法将启用一个新路径。在使用不同的子路径绘制新的线条之前,必须使用 beginPath() 来表明绘制的新起点。首次绘制线条时没有必要调用 beginPath() 方法。

moveTo() 方法会声明新的子路径的开始点。lineTo() 方法用于创建子路径。可以通过 lineWidthstrokeStyle 改变线条的外观。lineWidth 元素用于改变线条的粗细,而 strokeStyle 则用于改变线条的颜色。

在图 2 中,分别绘制了蓝色、绿色和紫色的线条。

图 2. 画布中有 3 条颜色不同的线条
画布中有 3 条颜色不同的线条

图 2 中的线条是通过 清单 1 中的代码创建的。蓝色的线条两端是圆角的,创建它的时候首先要确定一个新路径即将开始:context.beginPath()。其创建过程如下所示:

  • context.moveTo(50, 50),将路径的开始点置于 (x=50, y=50)
  • context.lineTo(300,50),识别线条的结束点
  • context.lineWidth = 10,线条的宽度
  • context.strokeStyle = "#0000FF",线条的颜色
  • context.lineCap = "round",使线条的两端变圆
  • context.stroke(),真实地将线条绘制在画布上

上面的 3 个线条的长度都是 50 像素,但它们看起来不一样长,这是由线条末端套盖 (cap) 引起的视觉混淆。有 3 种可用的末端套盖:

  • Context.round(蓝色)
  • Context.square(绿色)
  • Context.butt(紫色,即默认颜色)

butt 套盖是默认值。使用 round 或 square 套盖样式时,线条的长度将增加,增加的量为线条自身的宽度。例如,如果长度为 200 像素、宽度为 10 像素的线条采用 round 或 square 套盖样式,那么将导致线条的长度变为 210 像素,因为将在线条的两端各增加一个长度为 5 像素的套盖。如果长度为 200 像素、宽度为 20 像素的线条采用 round 或 square 套盖样式,那么将导致线条的长度变为 220 像素,因为将在线条的两端各增加一个长度为 10 像素的套盖。

运行并修改清单 1 的代码,以便更充分地了解如何绘制线条。

清单 1. 在画布上创建 3 个颜色不同的线条
<!DOCTYPE HTML>
<html>
    <head>
	    <title>Line Example</title>
        <style>
            body {
                margin: 0px;
                padding: 0px;
            }
            
            #myCanvas {
                border: 1px solid #9C9898;
            }
        </style>
        <script>
            
          window.onload = function() {
                var canvas = document.getElementById("myCanvas");
                var context = canvas.getContext("2d");
                
                // blue line with round ends
                context.beginPath();
                context.moveTo(50, 50);
                context.lineTo(300,50);
                context.lineWidth = 10;
                context.strokeStyle = "#0000FF"; 
                context.lineCap = "round";
                context.stroke();

                // green line with square ends
                context.beginPath();
                context.moveTo(50, 100);
                context.lineTo(300,100);
                context.lineWidth = 20;
                context.strokeStyle = "#00FF00"; 
                context.lineCap = "square";
                context.stroke();

                // purple line with butt ends
                context.beginPath();
                context.moveTo(50, 150);
                context.lineTo(300, 150);
                context.lineWidth = 30;
                context.strokeStyle = "#FF00FF"; 
                context.lineCap = "butt";
                context.stroke();
            };
        </script>
    </head>
    <body>
        <canvas id="myCanvas" width="400" height="200">
        </canvas>

    </body>
</html>

绘制矩形

在画布上绘制矩形区域有 3 种方法:

  • fillRect(x,y,width,height),绘制已填充的矩形
  • strokeRect(x,y,width,height),绘制矩形框线
  • clearRect(x,y,width,height),擦除矩形的给定区域使之完全透明

对于这 3 种方法而言,xy 表示矩形在画布上相对于左上角的位置(x=0, y=0),而 widthheight 分别是矩形的宽度和高度。

图 3 显示了由 清单 2 中的代码创建的 3 个矩形区域。

图 3. 矩形画布
被黄色填充的矩形,其内部有一个粗框黑体正方形和一个细框正方形

fillRect() 方法创建了一个矩形并使用默认的黑色填充它。clearRect() 方法在第一个矩形的中心擦除一块矩形区域,该区域位于 fillRect() 方法所创建的矩形的中心。strokeRect 方法创建一个仅黑色边框可见的矩形。

清单 2. 矩形画布代码
<!DOCTYPE HTML>
<html>
<head>
<title>Rectangle Example</title>
        <style>
            body {
                margin: 0px;
                padding: 0px;
            }
            
            #myCanvas {
                border: 1px solid #000000;
                background-color: #ffff00;
            }
        </style>
<script type="text/javascript">
function drawShape(){
   var canvas = document.getElementById('myCanvas');

    var context = canvas.getContext('2d');


    context.fillRect(25,25,50,50);
    context.clearRect(35,35,30,30);
    context.strokeRect(100,100,50,50);

}
</script>
</head>
<body onload="drawShape();">
   <canvas id="myCanvas" width="200" height="200"></canvas>
</body>
</html>

绘制弧形、曲线、圆形和半圆形

绘制圆形和半圆形都使用 arc() 方法。arc() 方法接收 6 个参数:

context.arc(centerX, centerY, radius, startingAngle, endingAngle, antiClockwise);

centerXcenterY 参数是圆心的坐标。radius 的含义和其数学上半径的含义一样:表示从圆心到圆周的直线距离。所创建的弧形将作为圆形的一部分。startAngleendAngle 参数分别为弧形的起点和终端,单位为弧度。anticlockwise 参数是一个布尔值。当其值为 true 时,弧形就逆时针绘制;当为 false 时,弧形就顺时针绘制。

要使用 arc() 方法绘制圆形,将起始角度定义为 0,结束角度为 2*PI,如下所示:

context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);

要使用 arc() 方法绘制半圆形,将结束角度定义为 startingAngle + PI,如下所示:

context.arc(centerX, centerY, radius, startingAngle, startingAngle + Math.PI, false);

二次方曲线

使用 quadraticCurveTo() 方法创建二次方曲线 ,如下所示。二次方曲线由上下文点、控制点和结束点定义。控制点确定线条的曲度。

context.moveTo(x, y);
context.quadraticCurveTo(controlX, controlY, endX, endY);

贝塞尔曲线

与二次方曲线一样,贝塞尔(Bezier)曲线也有一个起始点和一个结束点;但与二次方曲线不同的是,它有两个控制点:

context.moveTo(x, y);
context.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY);

使用 bezierCurveTo() 方法来创建贝塞尔曲线。因为贝塞尔曲线通过两个而不是一个控制点来定义,所以可以创建更加复杂的曲线。

图 4 从左到右分别显示了弧形、二次方曲线、贝塞尔曲线、半圆形和圆形。

图 4. 弧形、曲线和圆形
在画布上呈现弧形、曲线、圆形和半圆形的图

图 4 使用清单 3 中的代码创建。

清单 3. 弧形、曲线和圆形的代码
<!DOCTYPE HTML>
<html>
    <head>
	<title>Arcs, Curves, Circles, & Semicircles</title>
        <style>
            body {
                margin: 0px;
                padding: 0px;
            }
            
            #myCanvas {
                border: 1px solid #9C9898;
            }
        </style>
<script>
function drawArc(){
    var canvas = document.getElementById("myCanvas");
    var context = canvas.getContext("2d");
 
    var centerX = 100;
    var centerY = 160;
    var radius = 75;
    var startingAngle = 1.1 * Math.PI;
    var endingAngle = 1.9 * Math.PI;
    var counterclockwise = false;
 
    context.arc(centerX, centerY, radius, startingAngle, 
        endingAngle, counterclockwise);
 
    context.lineWidth = 10;
    context.strokeStyle = "black"; 
    context.stroke();
};

function drawQuadratic(){
    var canvas = document.getElementById("myCanvas");
    var context = canvas.getContext("2d");
 
    context.moveTo(200, 150);
 
    var controlX = 288;
    var controlY = 0;
    var endX = 388;
    var endY = 150;
 
    context.quadraticCurveTo(controlX, controlY, endX, endY);
    context.lineWidth = 10;
    context.strokeStyle = "black"; 
    context.stroke();
};

function drawBezier(){
    var canvas = document.getElementById("myCanvas");
    var context = canvas.getContext("2d");
 
    context.moveTo(350, 350);
 
    var controlX1 = 440;
    var controlY1 = 10;
    var controlX2 = 550;
    var controlY2 = 10;
    var endX = 500;
    var endY = 150;
 
    context.bezierCurveTo(controlX1, controlY1, controlX2, 
        controlY2, endX, endY);
 
    context.lineWidth = 10;
    context.strokeStyle = "black"; 
    context.stroke();
};

function drawCircle(){
    var canvas = document.getElementById("myCanvas");
    var context = canvas.getContext("2d");
 
    var centerX = 450;
    var centerY = 375;
    var radius = 70;
 
    context.beginPath();
    context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
 
    context.fillStyle = "#800000";
    context.fill();
    context.lineWidth = 5;
    context.strokeStyle = "black";
    context.stroke();
};


function drawSemicircle(){
    var canvas = document.getElementById("myCanvas");
    var context = canvas.getContext("2d");
 
    var centerX = 100;
    var centerY = 375;
    var radius = 70;
    var lineWidth = 5;
 
    context.beginPath();
    context.arc(centerX, centerY, radius, 0, Math.PI, false);
    context.closePath();
 
    context.lineWidth = lineWidth;
    context.fillStyle = "#900000";
    context.fill();
    context.strokeStyle = "black";
    context.stroke();
};

window.onload = function (){
drawArc();
drawQuadratic(); 
drawBezier(); 
drawCircle(); 
drawSemicircle()
}
</script>
    </head>
    <body>
        <canvas id="myCanvas" width="600" height="500">
        </canvas>
    </body>
</html>

变形、缩放和旋转

translate()scale()rotate() 方法都用于修改当前的图形。translate(x, y) 方法将画布上的元素移动到网格上的不同点。在 translate(x,y) 方法中,(x,y) 坐标表明图像在 x 轴和 y 轴方向上应该移动的像素数。

如果使用 drawImage() 方法在 (15,25) 位置上绘制图形,那么可以使用参数为 (20,30)translate() 方法,将图形放在 (15+20, 25+30) = (35, 55) 的位置上。

scale(x,y) 方法可改变图形的大小。x 参数指定水平缩放因素,而 y 参数指定垂直缩放因素。例如,scale(1.5, .75) 创建的图形比当前图形在 x 轴方向上大 50%,在 y 轴上大 75%。rotate(angle) 方法可根据指定的角度来选择对象。

图 5 展示了可以使用 translate()scale()rotate() 方法呈现的内容。

图 5. 使用变形
蓝色框中映照出从右到左的单词 mirror image

清单 4 提供了创建图 5 中的图形所需的代码。

清单 4. 创建变形的代码
<!DOCTYPE HTML>
<html>
<head>
<Title>Transformations Example</title>
<script>
 
window.onload = function() {
	var canvas=document.getElementById("myCanvas");
	var context=canvas.getContext("2d");
 
	var rectWidth = 250;
	var rectHeight = 75;
 
	// translate context to center of canvas
	context.translate(canvas.width/2,canvas.height/2); 		
 
	// half the y component 
	context.scale(1,0.5);

	// rotate 45 degrees clockwise
	context.rotate(-Math.PI/4); 
 
	context.fillStyle="blue";
	context.fillRect(-rectWidth/2,-rectHeight/2,
		rectWidth,rectHeight);


	// flip context horizontally
	context.scale(-1,1);
 
	context.font="30pt Calibri";
	context.textAlign="center";
	context.fillStyle="#ffffff";
	context.fillText("Mirror Image",3,10);

}
 
</script>
</head>
<body>
	<canvas id="myCanvas" width="400" height="400"></canvas>
</body>
</html>

渐变

渐变 就是从一种颜色过渡到另一种颜色的填充过程,两种颜色相交时会进行混合。可在画布中创建的渐变有两种:线性和辐射性的。

可以使用 createLinearGradient() 方法创建线性渐变。createLinearGradient(x0,y0,x1,y1) 沿着由两个点识别到的直线生成渐变:(x0,y0)(x1,y1) 分别是渐变的起点和终点。该方法返回一个对象。

彩色渐变可以使用多种颜色。addcolorStop(offset, color) 方法根据给定的偏移量指定颜色停止点。addColorStop() 方法允许指定介于 0 和 1 之间的偏移量,在该偏移量后将开始渐变到另一种颜色。值 0 是渐变的一端的偏移量;1 是渐变的另一端的偏移量。在定义了颜色渐变之后,就可以将渐变对象分配给 fillStyle()。还可以通过 fillText() 方法使用渐变绘制文本。

辐射渐变可以使用 createradialGradient(x0,y0,r0,x1,y1,r1) 来实现,用六个参数将两种或多种颜色以圆形或锥形的图案融合在一起:

  • (x0,y0) 圆锥的第一个圆形的中心
  • r0 第一个圆形的半径
  • (x1,y1) 圆锥的第二个圆形的中心
  • r1 第二个圆形的半径

图 6 包含 4 个渐变:一个线性渐变、一个文本渐变、一个对角线性渐变和一个辐射渐变。

图 6. 渐变示例
在画布上绘制渐变效果

图 6 由清单 5 中的代码创建。

清单 5. 渐变示例代码
<!doctype>
<html>
<head>
<title>Gradient Example</title>
<script>
   window.onload = function() {
      var canvas = document.getElementById("myCanvas");

      var context = canvas.getContext("2d");

      //Let's try the gradient on a rectangle

      // Create a linear gradient 
      var fillColor = context.createLinearGradient(50,50, 150,50);

      // Set  gradient colors
      fillColor.addColorStop(0.15,"red");
      fillColor.addColorStop(0.35,"black");
      fillColor.addColorStop(0.65,"green");
      fillColor.addColorStop(0.87,"yellow");

      // Assign gradient object to fillstyle
      context.fillStyle= fillColor;

      // Draw rectangle
      context.fillRect(50,50,100,100);

      // With text  

      var fillColorText = context.createLinearGradient(300,50,600,50);
 
      fillColorText.addColorStop(0.2,"red");
      fillColorText.addColorStop(0.4,"black");
      fillColorText.addColorStop(0.6,"green");
      fillColorText.addColorStop(0.8,"yellow");


     context.fillStyle= fillColorText;

      context.font="40px verdana";
      context.textBaseline="top";
      context.fillText("With text too!", 300,50)

      // Gradient on a diagonal
      var fillColordiagonal = context.createLinearGradient(50,200, 100,450);

      // Gradient colors
      fillColordiagonal.addColorStop(0.2,"red");
      fillColordiagonal.addColorStop(0.4,"black");
      fillColordiagonal.addColorStop(0.6,"green");
      fillColordiagonal.addColorStop(0.75,"yellow");

      // Assign gradient object to fillstyle
      context.fillStyle= fillColordiagonal;

      // Draw  rectangle
      context.fillRect(50,225, 100,250);

      // Draw radial gradient
     fillColorRadial = context.createRadialGradient(450,300,0, 450,300,200);
     fillColorRadial.addColorStop(0, "red");
     fillColorRadial.addColorStop(0.2, "black");
     fillColorRadial.addColorStop(0.4, "green");
     fillColorRadial.addColorStop(0.7, "yellow");
     context.fillStyle = fillColorRadial;
     context.rect(300,200,500,400);
     context.fill();

}
</script>
</head>
<body>
<div>
    <p><canvas id="myCanvas" width="600" height="400"></canvas></p>
</div>
</body>
</html>

图像裁剪

可以通过裁剪选中的区域来改变图像。要在画布上进行裁剪,则需要重载 drawImage() 方法。drawImage() 方法有 3 个选项。您可以使用 3 个、5 个或 9 个参数。

3 个参数配置,即 drawImage(image, dx, dy),可将图像绘制在画布的目标坐标 (dx, dy) 上。该坐标构成图像的左上角。

5 个参数配置,即 drawImage(image, dx, dy, dw, dh),可为目标坐标提供宽度和高度。将缩放图像以适合目标宽度和高度。

9 个参数配置,即 drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh),可从一个图像中剪切一个矩形区域,该区域的原坐标为 (sx,sy),宽度和高度为 (sw,sh),然后缩放该区域使之适合于目标宽度和高度 (dw,dh),并将其放置到画布的 (dx,dy) 位置上。

图 7 显示了要剪切的图像。

图 7. 剪切图像
描绘马背上的拿破仑

以图 7 中的图像为背景将一组图像放到画布上。选用与画布大小相同的图像作为背景。另一个要创建的图像则应更小一些,将插入画布的右下角。第三个图像是拿破仑头部的剪切,放置于画布的左上角。图 8 显示了裁剪之后的图像。

图 8. 裁剪之后的图像
同一张拿破仑图像,但左上角是头部截图,右下角是缩略图

图 8 由清单 6 中的代码创建。在运行该代码之前,确保下载本例中要使用的 Napolean.png 图像

清单 6. 裁剪实例图像的代码
<!doctype>
<html>
<head>
<title>Crop Example</title>
<script type="text/javascript">
  window.onload = function()  {
  var canvas=document.getElementById("cropNapolean");
	var context=canvas.getContext("2d");
 
	var imageObj = new Image();
	imageObj.onload = function() {
	// draw image to cover the entire canvas
		context.drawImage(imageObj,0,0, 600, 400);
 
	// draw small image in bottom right corner
		var sourceX = 0;
		var sourceY = 0;
		var sourceWidth = 1200;
		var sourceHeight = 801;
		var destX = 300;
		var destY = 200;
		var destWidth = sourceWidth - 900;
		var destHeight = sourceHeight - 600;
 
		context.drawImage(imageObj, sourceX, sourceY, sourceWidth,
			sourceHeight, destX, destY, destWidth, destHeight);
	
	 //draw Napolean's head only
		var sourceNapoleanX = 460;
		var sourceNapoleanY = 25;
		var sourceNapoleanWidth = 250;
		var sourceNapoleanHeight = 175;
		var destNapoleanX = 0;
		var destNapoleanY = 0;
		var destNapoleanWidth = sourceNapoleanWidth - 150 ;
		var destNapoleanHeight = sourceNapoleanHeight - 100;
 
        context.drawImage(imageObj, sourceNapoleanX, sourceNapoleanY, 
             sourceNapoleanWidth, sourceNapoleanHeight, 
               destNapoleanX, destNapoleanY, 
                 destNapoleanWidth, destNapoleanHeight);
	}
	imageObj.src = "Napoleon.png";	
}
</script>
  
  </head>
<body>
  <div>
    <p><canvas id="cropNapolean" width="600" height="400"></canvas></p>
  </div>
</body>
</html>

动画和多个画布

在处理动画时,层是经常遇到问题的地方。层允许隔离组件,使编写和调试代码变得更容易、更高效。Canvas API 没有层,但它可以创建多个画布。

必须通过时间来控制动画。因此,要创建动画必须实现动画的每个帧。Canvas API 在动画方面有一个主要的局限性:一旦在画布上创建了图形,将无法再改变它。要移动该图形,就必须重新绘制它。

创建动画的流程:

  1. 清除先前在画布上绘制的所有形状。
  2. 保存画布状态,确保每次绘制帧是使用的都是最初状态。
  3. 执行呈现帧的步骤。
  4. 如果已保存了状态,请在绘制新的帧之前恢复它。

可以通过两种方式来控制动画:使用 setIntervalsetTimeout 函数,两个函数均可在固定的时间段内调用函数。setInterval 函数重复执行所提供的代码。setTimeout 函数仅在所提供的时间段到达时调用一次。

图 9 显示了游泳者的多画布动画中的一帧。水在一个画布上,而游泳者在另一个画布上。

图 9. 在多个画布上使用图像的动画
将图像放在蓝色渐变背景上

使用清单 7 中的代码来创建游泳者 (swimmer)。游泳者使用线性渐变来创建水 (water)。水有 4 种蓝色阴影,这就产生了类似水的视觉。然后可以使用 positionXpositionY 值来创建游泳者的动画,这将改变图像的姿势。使用 arc() 方法来创建游泳者的头部。通过绘制直线创建游泳者的胳膊和腿,然后在改变它们的 lineTo() 位置。再通过改变 moveTo() 的位置来改变躯干。由于这是动画,所以必须运行代码才能看到游泳者的游泳动作。

清单 7. 动画示例
<!DOCTYPE HTML>
<html>
    <head>
	<title>Animation & Multiple Canvas Example</title>
<script>            
// Water canvas
function  drawWater() {
    var canvasWater = document.getElementById("myWaterCanvas");
    var contextWater = canvasWater.getContext("2d");
	contextWater.globalAlpha = .50 ;

    // Create a linear gradient fill
    var linearGrad = contextWater.createLinearGradient(0,0,400,400);
    linearGrad.addColorStop(0, '#0000ff'); // sets the first color
    linearGrad.addColorStop(.25, '#0099ff'); // sets the second color
    linearGrad.addColorStop(.50, '#00ccff'); // sets the third color
    linearGrad.addColorStop(.75, '#00ffff'); // sets the fourth color
    contextWater.fillStyle = linearGrad;
    contextWater.fillRect(0,0,400,400);
}

// Swimmer canvas
           setInterval(drawSwimmer, 30);
           var positionX = 0;
           var positionY = 0;
            
          function  drawSwimmer(){
                var canvasSwimmer = document.getElementById("mySwimmerCanvas");
                var contextSwimmer = canvasSwimmer.getContext("2d");
                contextSwimmer.clearRect(0,0,400,400);

                if (positionX < 30)
                  {
                     positionX += 1;
                     positionY += 1;
                  }
                else
                {
                     positionX = 0;
                     positionY = 0;
                }
               

                contextSwimmer.save();

               // draw circle for head
               var centerX = 200;
               var centerY = 50;
               var radius = 20;
 
               contextSwimmer.beginPath();
               contextSwimmer.arc(centerX, centerY+positionY, 
			                         radius, 0, 2 * Math.PI, false);
 
               contextSwimmer.fillStyle = "#000000";
               contextSwimmer.fill();
               contextSwimmer.lineWidth = 5;


                // torso
                contextSwimmer.beginPath();
                contextSwimmer.moveTo(200,70+positionY);
                contextSwimmer.lineTo(200,175);
                contextSwimmer.lineWidth = 10;
                contextSwimmer.strokeStyle = "#000000"; 
                contextSwimmer.lineCap = "round";
                contextSwimmer.stroke();

               // image right arm
                contextSwimmer.beginPath();
                contextSwimmer.moveTo(200, 100);
                contextSwimmer.lineTo(175-positionX,140-positionY);
                contextSwimmer.lineWidth = 10;
                contextSwimmer.strokeStyle = "#000000"; 
                contextSwimmer.lineCap = "round";
                contextSwimmer.stroke();

               // image left arm
                contextSwimmer.beginPath();
                contextSwimmer.moveTo(200, 100);
                contextSwimmer.lineTo(225+positionX,140-positionY);
                contextSwimmer.lineWidth = 10;
                contextSwimmer.strokeStyle = "#000000"; 
                contextSwimmer.lineCap = "round";
                contextSwimmer.stroke();

               // image right leg
                contextSwimmer.beginPath();
                contextSwimmer.moveTo(200, 175);
                contextSwimmer.lineTo(190-positionX,250-positionY);
                contextSwimmer.lineWidth = 10;
                contextSwimmer.strokeStyle = "#000000"; 
                contextSwimmer.lineCap = "round";
                contextSwimmer.stroke();


               // image left leg
                contextSwimmer.beginPath();
                contextSwimmer.moveTo(200, 175);
                contextSwimmer.lineTo(210+positionX,250-positionY);
                contextSwimmer.lineWidth = 10;
                contextSwimmer.strokeStyle = "#000000"; 
                contextSwimmer.lineCap = "round";
                contextSwimmer.stroke();

                contextSwimmer.restore();

           };

</script>

</head>
    <body onload="drawWater();">
        <canvas id="myWaterCanvas" width="400" height="400" style="z-index: 2; 
		              position:absolute;left:0px;top:0px;">
        </canvas>
        <canvas id="mySwimmerCanvas" width="400" height="400" style="z-index: 1; 
		              position:absolute;left:0px;top:0px;">
        </canvas>

    </body>
</html>

结束语

HTML5 画布对于构建基于浏览器的 RIA 至关重要。它提供受 JavaScript 支持的实用绘制环境,便于您施展自己的想象力。HTML5 学习起来并不困难,并且网上有许多支持工具能够满足培训和学习需要,包括解答、博客、在线文章、视频和非视频教程和样例应用程序。

能够以可视的方式修改文本、图像和模拟动画,这些让 Canvas 成为极有价值的工具。不管是设计者还是开发人员,也不管是使用 Canvas 构建运行在移动设备上的游戏应用程序,还是仅仅使用它改善网页的布局,Canvas 都是 HTML5 体验的重头戏。


下载

描述名字大小
Napoleon 图像Napoleon.zip2045KB

参考资料

学习

获得产品和技术

  • Dojo Toolkit 是一个开放源码的模块化 JavaScript 库,能够帮助您快速开发跨平台的、基于 JavaScript/Ajax 的应用程序和网站。
  • 免费使用 IBM 软件。下载试用版本,通过登录在线试用,在沙箱环境中使用产品或通过云进行访问。您可以选择试用超过 100 款 IBM产品。

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=826462
ArticleTitle=HTML5 基础知识,第 4 部分: 最后的完善
publish-date=07182012