(譯者注:由于yeeyan編輯器對(duì)文章中的標(biāo)簽做解析的原因,我在每個(gè)標(biāo)簽的<符號(hào)之后都加入了一個(gè)空格,比如說,左尖括號(hào)<+head+右尖括號(hào)>,我會(huì)寫成< head>,以便其能夠在文章中正確顯示,不便之處敬請(qǐng)諒解。)
使用HTML5來編寫代碼的人,有著設(shè)計(jì)者和開發(fā)者雙重身份的強(qiáng)悍組合,其職責(zé)是構(gòu)造出高效的富互聯(lián)網(wǎng)應(yīng)用(rich Internet application,RIA),特別是豐富的用戶界面。就高效這個(gè)字眼來說,我的意思是指系統(tǒng)級(jí)的和系統(tǒng)性的創(chuàng)造力增強(qiáng),這種增強(qiáng)以數(shù)字化的方式促進(jìn)了站點(diǎn)所有者、所有者的代理機(jī)構(gòu)和站點(diǎn)用戶之間的對(duì)話。
RIA是用戶獲得滿意體驗(yàn)的來源之處和媒介,因此,它是任何成功的以網(wǎng)絡(luò)為中心的風(fēng)險(xiǎn)投資的重要組成部分。以網(wǎng)絡(luò)為中心的活動(dòng),就性質(zhì)來說,或多或少都是協(xié)作式的。公司要在包括了市場(chǎng)營(yíng)銷和管理的各個(gè)層面都取得成功的話,數(shù)字化協(xié)作的制勝方法是至關(guān)重要的。很多時(shí)候的很多情況都取決于效率,網(wǎng)站要依靠效率來滿足其訪問者的品質(zhì)期望。
正如你已經(jīng)見到的那樣,HTML5是為這一具有跨平臺(tái)能力、融合了通信、使用統(tǒng)一語(yǔ)言、提供無處不在的計(jì)算,以及基于開放系統(tǒng)的協(xié)作式“一網(wǎng)化世界(one web world)”量身定做的。這一文章系列的前面三部分內(nèi)容重點(diǎn)關(guān)注語(yǔ)義、正確的編碼方法、輸入在極為重要的轉(zhuǎn)化過程中的作用,以及站點(diǎn)管理最佳做法等,所有這些的目的都是在為以一種有組織和符合邏輯的方式來創(chuàng)建RIA奠定基礎(chǔ)。每篇文章中都共有的一個(gè)主題是,對(duì)于實(shí)現(xiàn)網(wǎng)站所有者的機(jī)構(gòu)目標(biāo)來說,制造并管理豐富的用戶體驗(yàn)是至關(guān)重要的。
什么是Canvas?
HTML5 Canvas(畫布)是一個(gè)非常有用的繪圖和動(dòng)畫元素,Canvas使用JavaScript來直接在頁(yè)面上繪制圖形。這是一個(gè)由你來定義和控制的長(zhǎng)方形區(qū)域,該區(qū)域允許動(dòng)態(tài)、可腳本渲染的2D圖形和位圖圖像。
在制作用來增強(qiáng)UI、示意圖、相冊(cè)、圖表、圖形、動(dòng)畫和嵌入式繪圖應(yīng)用的那些非常棒的視覺材料方面,HTML5堪稱完美。Canvas元素有一些用來繪制路徑、矩形、圓形和字符的方法。
Canvas的坐標(biāo)
在畫布上繪圖的一個(gè)先決條件是要熟悉網(wǎng)格或是坐標(biāo)空間,寬度和高度的空間區(qū)域測(cè)量是以像素為單位給出的。畫布是基于x和y坐標(biāo)的使用來構(gòu)建的,畫布的x=0, y=0坐標(biāo)位于左上角。
畫布的矩形區(qū)域的默認(rèn)屬性是300像素的寬度和150像素的高度,但你可以通過指定寬度和高度來確定畫布元素的確切大小。圖1中的示意圖說明了x和y坐標(biāo)的實(shí)現(xiàn)方式。
圖1. Canvas的坐標(biāo)
圖1給出了一個(gè)100像素X100像素的畫布區(qū):
1. 左上角是x=0,y=0。
2. x的值水平增加,y的值垂直增加。
3. 右下角是x=100,y=100。
4. 中間的點(diǎn)是x=50,y=50。
開始第一步
要在畫布上放置任何東西的話,你首先必須在HTML文件中定義畫布。你必須創(chuàng)建訪問< canvas>標(biāo)簽的JavaScript代碼,并通過與HTML5 Canvas API通信來繪制你的圖像。
< canvas>標(biāo)簽的基本結(jié)構(gòu)如下:
< canvas id="myCanvas" width="200" height="200">< /canvas>
canvas元素自身有兩個(gè)屬性:width和height,除此之外,canvas還擁有所有主要的HTML5屬性,比如說class、id和name等。id屬性被用在上面所示的代碼中,JavaScript使用這里創(chuàng)建的canvas的id來表示要在上面繪畫的畫布。JavaScript使用document.getElementById()方法來確定正確的畫布,如下面代碼所示:
var canvas = document.getElementById("myCanvas");
每個(gè)畫布都必須要有一個(gè)context(上下文)的定義,如下面代碼所示。就目前的情況來說,官方規(guī)范只承認(rèn)一個(gè)2D環(huán)境:
var context = canvas.getContext("2d");
在標(biāo)識(shí)畫布并指明了它的上下文之后,你就做好了開始繪畫的準(zhǔn)備了。
繪圖工具、效果和轉(zhuǎn)換
在HTML5 Canvas的這一討論過程中,我們對(duì)各種繪圖工具、效果和轉(zhuǎn)換都查看一番。繪圖工具包括:
1. 線條
2. 矩形
3. 圓弧
4. 貝塞爾曲線和二次曲線
5. 圓和半圓
你會(huì)用到的Canvas效果包括:
1. 填充和描邊
2. 線性和徑向的漸變
要討論的轉(zhuǎn)換包括:
1. 縮放
2. 旋轉(zhuǎn)
3. 平移
繪制線段
要在畫布上繪制線段的話,你可以使用moveTo()、lineTo()和stroke()方法,此外,你要使用beginPath()方法來重置當(dāng)前路徑:
1. context.beginPath();
2. Context.moveTo(x,y);
3. Context.lineTo(x,y);
4. Context.stroke(x,y);
beginPath()方法開始一條新的路徑,在使用不同的子路徑繪制一條新的線段之前,你必須要使用beginPath()來標(biāo)明一個(gè)繪制過程要遵循的新起點(diǎn)。在繪制第一條線段時(shí),beginPath()方法的調(diào)用不是必須的。
moveTo()方法指明新的子路徑從哪里開始,lineTo()方法創(chuàng)建子路徑。你可以使用lineWidth和strokeStyle來改變線段的外觀,lineWidth元素改變線段的粗細(xì),strokeStyle改變顏色。
在圖2中,三條線段分別用藍(lán)色、綠色和紫色畫了出來。
圖2. 畫有三條不同顏色的線段的畫布
圖2中的線段由清單1中的代碼來創(chuàng)建,藍(lán)色的線段有著圓弧形的端點(diǎn),該線段是由首個(gè)context.beginPath()這一開始新路徑的建立的方法來創(chuàng)建的,其后緊跟著:
1. context.moveTo(50, 50),該方法把線路的起點(diǎn)置于(x=50, y=50)
2. context.lineTo(300,50),該方法標(biāo)識(shí)線段的終點(diǎn)
3. context.lineWidth = 10,該屬性是線段的寬度
4. context.strokeStyle = "#0000FF",該屬性是線段的顏色
5. context.lineCap = "round",該屬性把端點(diǎn)設(shè)成是圓弧狀的
6. context.stroke(),該方法真正在畫布上繪制該線段
所有線段的長(zhǎng)度都是50像素,盡管它們看上去不一樣長(zhǎng)——這是由線段的線帽(line cap)造成的視覺錯(cuò)覺。可用的線帽有三種:
1. Context.round (blue)
2. Context.square (green)
3. Context.butt (purple)——默認(rèn)值
對(duì)接(butt)線帽是默認(rèn)值,當(dāng)你使用圓形(round)或是方形(square)的線帽風(fēng)格時(shí),線段的長(zhǎng)度會(huì)增加,加上一段相當(dāng)于線段寬度的長(zhǎng)度。例如,一個(gè)長(zhǎng)度為200像素,寬度為10像素,有著圓形或是方形線帽風(fēng)格的線段,其最終的線段長(zhǎng)度是210像素,因?yàn)槊總€(gè)線帽都都往線段的每一端加上了5個(gè)像素的長(zhǎng)度。而一個(gè)長(zhǎng)度為200像素,寬度為20像素,有著圓形或是方形的線帽風(fēng)格的線段的最終長(zhǎng)度是220像素,因?yàn)槊總€(gè)線帽都往線段每一端加上了10像素的長(zhǎng)度。
通過執(zhí)行和修改清單1中的代碼來更好地理解線段的繪制方式。
清單1. 在畫布上創(chuàng)建三條不同顏色的線段
< !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");
// 有著圓形端點(diǎn)的藍(lán)色線段
context.beginPath();
context.moveTo(50, 50);
context.lineTo(300,50);
context.lineWidth = 10;
context.strokeStyle = "#0000FF";
context.lineCap = "round";
context.stroke();
// 有著方形端點(diǎn)的綠色線段
context.beginPath();
context.moveTo(50, 100);
context.lineTo(300,100);
context.lineWidth = 20;
context.strokeStyle = "#00FF00";
context.lineCap = "square";
context.stroke();
// 有著對(duì)接端點(diǎn)的紫色線段
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>
繪制矩形
有三個(gè)方法可用來在畫布上給出一個(gè)矩形的區(qū)域:
1. fillRect(x,y,width,height),該方法繪制一個(gè)有填充的矩形
2. strokeRect(x,y,width,height),該方法繪制一個(gè)矩形的外邊框
3. clearRect(x,y,width,height),該方法清空指定的區(qū)域,使之變得完全透明
對(duì)于這三個(gè)方法中的每個(gè)來說,x和y表示的都是畫布上相對(duì)于矩形(x=0, y=0)的左上角的位置,width和height分別是矩形的寬度和高度。
圖3顯示了由清單2中的代碼創(chuàng)建的三個(gè)矩形。
圖3. 畫有矩形的畫布
fillRect()方法創(chuàng)建了一個(gè)以缺省的黑色為填充色的矩形;clearRect()方法在第一個(gè)矩形的中心部分清除出一個(gè)矩形區(qū)域,該區(qū)域位于由fillRect()方法產(chǎn)生的矩形的中央位置;strokeRect創(chuàng)建了一個(gè)只有可見的黑色邊框的矩形。
清單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()方法用到了六個(gè)參數(shù):
context.arc(centerX, centerY, radius, startingAngle, endingAngle, antiClockwise);
centerX和centerY參數(shù)是圓的中心坐標(biāo),radius就是數(shù)學(xué)上的半徑:從圓心到圓周線的一條直線?;⌒问亲鳛樗x的圓的一部分來創(chuàng)建的,startAngle和endAngle參數(shù)分別是圓弧的起點(diǎn)和終點(diǎn),以弧度為單位。anticlockwise參數(shù)是一個(gè)布爾(Boolean)值,當(dāng)其值為true時(shí),弧形按逆時(shí)針方向來繪制,當(dāng)其值為false時(shí),弧形按順時(shí)針方向來繪制。
要使用arc()方法來繪制圓的話,把起始角度定義成0,把結(jié)束角度定義成2*PI,如下所示:
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
要使用arc()方法來繪制半圓的話,把結(jié)束角度定義成startingAngle + PI,如下所示:
context.arc(centerX, centerY, radius, startingAngle, startingAngle + Math.PI, false);
二次曲線
quadraticCurveTo()方法被用來創(chuàng)建一條二次曲線,如下所示。二次曲線通過上下文中的點(diǎn)、一個(gè)控制點(diǎn)以及一個(gè)結(jié)束點(diǎn)來定義??刂泣c(diǎn)確定了線的曲度。
context.moveTo(x, y);
context.quadraticCurveTo(controlX, controlY, endX, endY);
貝塞爾曲線
正和二次曲線一樣,貝塞爾曲線也有一個(gè)起點(diǎn)和一個(gè)終點(diǎn),但和二次曲線不同的是,它有兩個(gè)控制點(diǎn):
context.moveTo(x, y);
context.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY);
你可使用bezierCurveTo()方法來創(chuàng)建貝塞爾曲線,因?yàn)樨惾麪柷€是由兩個(gè)控制點(diǎn)而不僅是由一個(gè)控制點(diǎn)來定義的,所有你可以創(chuàng)造出更加復(fù)雜的曲度來。
圖4的顯示——從左到右——為一條圓弧、一條二次曲線、一條貝塞爾曲線、一個(gè)半圓和一個(gè)圓。
圖4. 圓弧、曲線和圓
圖4的內(nèi)容是用清單3中的代碼來創(chuàng)建的。
清單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>
轉(zhuǎn)換:平移、縮放和旋轉(zhuǎn)
translate()、scale()和rotate()方法都會(huì)修改當(dāng)前的矩陣。translate(x, y)方法把畫布上的項(xiàng)目移動(dòng)到網(wǎng)格上的不同點(diǎn)上,在translate(x, y)方法中,(x,y)坐標(biāo)指明了圖像在x方向和y方向上應(yīng)該移動(dòng)的像素?cái)?shù)。
如果你使用drawImage()方法來在(15,25)這一位置繪制一個(gè)圖像的話,你可以使用(20,30)作為參數(shù)的來調(diào)用translate(),該調(diào)用把圖像放在(15+20, 25+30) = (35, 55)這一位置上。
scale(x,y)方法改變圖像的大小,x參數(shù)指明水平方向的比例系數(shù),y參數(shù)指明垂直方向的比例系數(shù)。例如,scale(1.5, .75)將創(chuàng)建一個(gè)在x方向加大50%,而在y方向只相當(dāng)于當(dāng)前尺寸75%的圖像。rotate(angle)方法返回一個(gè)基于指定角度的對(duì)象。
圖5是一個(gè)可以使用translate()、scale()和rotate()進(jìn)行渲染的圖像例子。
圖5. 使用轉(zhuǎn)換
清單4提供的代碼創(chuàng)建了圖5中的圖像。
清單4. 創(chuàng)建轉(zhuǎn)換的代碼
< !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;
// 把context平移到畫布的中心
context.translate(canvas.width/2,canvas.height/2);
// y方向的組成減半
context.scale(1,0.5);
// 順時(shí)針旋轉(zhuǎn)45度
context.rotate(-Math.PI/4);
context.fillStyle="blue";
context.fillRect(-rectWidth/2,-rectHeight/2,
rectWidth,rectHeight);
// 水平方向翻轉(zhuǎn)context
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>
漸變
漸變(gradient)是指從一種顏色向另一種顏色變化的填充,在顏色相交的地方做融合。在Canvas中你可以創(chuàng)建兩種類型的漸變:線性的和徑向的。
createLinearGradient()方法被用來創(chuàng)建線性的漸變。createLinearGradient(x0,y0,x1,y1)沿著一條由兩個(gè)點(diǎn)(x0,y0)和(x1,y1)來標(biāo)識(shí)的直線產(chǎn)生一個(gè)漸變,這兩個(gè)點(diǎn)分別是漸變的起點(diǎn)和終點(diǎn)。該方法返回一個(gè)對(duì)象。
顏色的漸變可以有多種顏色,addcolorStop(offset, color) 方法為被標(biāo)明為在給定的偏移量上漸變的顏色指明了顏色過渡點(diǎn)。addColorStop()方法讓你在0和1之間指定一個(gè)偏移量,以這一偏移量為依據(jù)來開始過渡到下一種顏色。值0是漸變的一端的偏移量,1是另一端的偏移量。在顏色的漸變定義好了之后,漸變對(duì)象就可以被賦值給fillStyle()。你也可以使用fillText()方法來繪制出帶有漸變的文字來。
徑向漸變——createradialGradient(x0,y0,r0,x1,y1,r1)——使用六個(gè)參數(shù)以一種圓形或是圓錐形的模式來組合兩種或多種顏色。
1. (x0,y0): 圓錐的第一個(gè)圓的中心
2. r0:第一個(gè)圓的半徑
3. (x1,y1):圓錐的第二個(gè)圓的中心
4. r1:第二個(gè)圓的半徑
圖6包含了四種漸變:一個(gè)線性漸變、一個(gè)文本漸變、一個(gè)對(duì)角線上的漸變和一個(gè)徑向漸變。
圖6. 漸變的例子
圖6的內(nèi)容是使用清單5中的代碼創(chuàng)建出來的。
清單5. 漸變的例子代碼
< !doctype>
< html>
< head>
< title>Gradient Example< /title>
< script>
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
//在一個(gè)矩形中嘗試做漸變
// 創(chuàng)建一個(gè)線性漸變
var fillColor = context.createLinearGradient(50,50, 150,50);
// 設(shè)置漸變的顏色
fillColor.addColorStop(0.15,"red");
fillColor.addColorStop(0.35,"black");
fillColor.addColorStop(0.65,"green");
fillColor.addColorStop(0.87,"yellow");
// 把漸變對(duì)象賦值給fillstyle
context.fillStyle= fillColor;
// 繪制矩形
context.fillRect(50,50,100,100);
// 使用文本
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)
// 對(duì)角線上的漸變
var fillColordiagonal = context.createLinearGradient(50,200, 100,450);
// 漸變顏色
fillColordiagonal.addColorStop(0.2,"red");
fillColordiagonal.addColorStop(0.4,"black");
fillColordiagonal.addColorStop(0.6,"green");
fillColordiagonal.addColorStop(0.75,"yellow");
// 把漸變對(duì)象賦值給fillstyle
context.fillStyle= fillColordiagonal;
// 繪制矩形
context.fillRect(50,225, 100,250);
// 繪制徑向漸變
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>
圖像剪裁
你可以通過裁剪出選定的區(qū)域來改變圖像。在畫布上裁剪是一項(xiàng)重載drawImage()方法的功能,drawImage()有三種選擇,你可以使用三個(gè)、五個(gè)或者是九個(gè)參數(shù)。
三個(gè)參數(shù)的配置——drawImage(image, dx, dy)——在目標(biāo)坐標(biāo)(dx,dy)上繪制圖形。坐標(biāo)構(gòu)成了圖像的左上角。
五個(gè)參數(shù)的配置——drawImage(image, dx, dy, dw, dh)——提供了目標(biāo)的寬度和高度,圖像會(huì)被縮放以適應(yīng)目標(biāo)寬度和高度。
九個(gè)參數(shù)的配置——drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)——用到一個(gè)圖像,以圖像來源的(sx,sy)坐標(biāo)為開始剪出一個(gè)寬度和高度為(sw,sh)的矩形區(qū)域,并把它縮放使之適應(yīng)目標(biāo)寬度和高度(dw,dh),然后把它放置在畫布的(dx,dy)位置上。
圖7顯示了你將要對(duì)其做剪裁的圖像。
圖7. 剪裁圖像
通過利用圖7中給出的圖像,可以把一組圖像放置在畫布上。一個(gè)圖像有畫布大小,被用作背景,另一個(gè)被創(chuàng)建的圖像較小一些,被插入到畫布的右下角上,第三個(gè)圖像是一個(gè)切出來的拿破侖的頭像,被放置在畫布的左上角上。裁剪后的圖像的最后情況如圖8所示。
圖8. 最終裁剪出來的圖像
圖8中的內(nèi)容是使用清單6中的代碼創(chuàng)建出來的。在執(zhí)行這一代碼之前,確保已下載了這一例子中用到的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() {
// 繪制圖像覆蓋整個(gè)畫布
context.drawImage(imageObj,0,0, 600, 400);
// 在右下角繪制一個(gè)小圖像
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);
//只繪制拿破侖的頭部
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>
動(dòng)畫和多重畫布
要處理動(dòng)畫方面的內(nèi)容的話,分層問題總是不可避免的。分層允許組件被隔開開來,這使得編碼和調(diào)試變得更容易且更高效。Canvas API并未有分層的處理,但你可以創(chuàng)建多重的畫布。
動(dòng)畫必須是隨著時(shí)間的推移來做控制的,因此,要?jiǎng)?chuàng)建一個(gè)動(dòng)畫的話,你需要處理動(dòng)畫的每一幀內(nèi)容。Canvas API在動(dòng)畫方面有一個(gè)主要的限制是:在某個(gè)形狀被放置到畫布上之后,它就一直保持它的樣子不變了,要移動(dòng)該形狀的話,你必須要重新繪制它。
要?jiǎng)?chuàng)建一個(gè)動(dòng)畫的話:
1. 清除掉之前在畫布上繪制的任何圖像。
2. 保存畫布的狀態(tài),確保在每次繪制一個(gè)幀的時(shí)候都是使用最初的狀態(tài)。
3. 執(zhí)行渲染幀的步驟。
4. 如果你已經(jīng)保存了狀態(tài)的話,在繪制新的幀之前恢復(fù)該狀態(tài)。
你可以以兩種方式來控制動(dòng)畫:使用setInterval或者setTimeout方法,每個(gè)方法都可以用來在超過某個(gè)設(shè)定時(shí)間段時(shí)調(diào)用一個(gè)函數(shù)。setInterval函數(shù)重復(fù)地執(zhí)行所提供的代碼,setTimeout函數(shù)只在所提供的時(shí)間過去之后執(zhí)行一次。
圖9展示了游泳者的多重畫布動(dòng)畫的一幀,水畫在一幅畫布上,游泳的人則畫在另一幅畫布上。
圖9. 用到多重畫布的圖像的動(dòng)畫
清單7中的代碼被用來創(chuàng)建游泳者,代碼使用一個(gè)線性漸變來創(chuàng)建水的效果。水有四種藍(lán)色色調(diào),這提供了一種合理的水的假象。游泳者的動(dòng)作通過使用positionX和positionY的值來創(chuàng)建,這兩個(gè)值改變圖像所擺放的樣子。游泳者的頭使用arc()方法來創(chuàng)建,游泳者的腿和雙臂則是通過繪制線段然后改變他們的lineTo()位置來創(chuàng)建,軀干則是通過修改moveTo()的位置來發(fā)生變化。因?yàn)檫@是一個(gè)動(dòng)畫,因此你需要執(zhí)行這一段代碼來看一下游泳者是如何運(yùn)動(dòng)的。
清單7. 動(dòng)畫例子
< !DOCTYPE HTML>
< html>
< head>
< title>Animation & Multiple Canvas Example< /title>
< script>
// 水的畫布
function drawWater() {
var canvasWater = document.getElementById("myWaterCanvas");
var contextWater = canvasWater.getContext("2d");
contextWater.globalAlpha = .50 ;
// 創(chuàng)建一個(gè)線性漸變的填充
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);
}
// 游泳者的畫布
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();
// 繪制一個(gè)圓作為頭部
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;
// 軀干部分
contextSwimmer.beginPath();
contextSwimmer.moveTo(200,70+positionY);
contextSwimmer.lineTo(200,175);
contextSwimmer.lineWidth = 10;
contextSwimmer.strokeStyle = "#000000";
contextSwimmer.lineCap = "round";
contextSwimmer.stroke();
// 畫右邊的手臂
contextSwimmer.beginPath();
contextSwimmer.moveTo(200, 100);
contextSwimmer.lineTo(175-positionX,140-positionY);
contextSwimmer.lineWidth = 10;
contextSwimmer.strokeStyle = "#000000";
contextSwimmer.lineCap = "round";
contextSwimmer.stroke();
// 畫左邊的手臂
contextSwimmer.beginPath();
contextSwimmer.moveTo(200, 100);
contextSwimmer.lineTo(225+positionX,140-positionY);
contextSwimmer.lineWidth = 10;
contextSwimmer.strokeStyle = "#000000";
contextSwimmer.lineCap = "round";
contextSwimmer.stroke();
// 畫右邊的腿
contextSwimmer.beginPath();
contextSwimmer.moveTo(200, 175);
contextSwimmer.lineTo(190-positionX,250-positionY);
contextSwimmer.lineWidth = 10;
contextSwimmer.strokeStyle = "#000000";
contextSwimmer.lineCap = "round";
contextSwimmer.stroke();
// 畫左邊的腿
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>
結(jié)論
HTML5的畫布是基于瀏覽器的RIA的結(jié)構(gòu)核心,其提供了一種以JavaScript和你的想像力為驅(qū)動(dòng)力的實(shí)用的繪圖環(huán)境。學(xué)習(xí)它真的不是很難,并且網(wǎng)絡(luò)上有許多培訓(xùn)和學(xué)習(xí)所需的支持工具,其中包括了速查表、博客、在線文章、視頻和非視頻教程,以及示例應(yīng)用程序等。
可視化地修改文本和圖像以及模擬運(yùn)動(dòng)的能力使得Canvas變成了一個(gè)極其有價(jià)值的工具,無論你是從設(shè)計(jì)者還是開發(fā)者的角度來熟悉它,無論你是使用Canvas來構(gòu)建運(yùn)行在移動(dòng)設(shè)備上的游戲應(yīng)用,還是僅僅想增強(qiáng)屏幕這一整體資源的利用情況,Canvas都是HTML5體驗(yàn)的重要組成部分。