十四、使用 Canvas 繪圖

??HTML5 添加的最受歡迎的功能就是<canvas>元素。這個(gè)元素負(fù)責(zé)在頁(yè)面中設(shè)定一個(gè)區(qū)域,然后就可以通過(guò) JavaScript 動(dòng)態(tài)地在這個(gè)區(qū)域中繪制圖形。

??<canvas>元素最早是由蘋(píng)果公司推出的,當(dāng)時(shí)主要用在其 Dashboard 微件中。

??很快,HTML5 加入了這個(gè)元素,主流瀏覽器也迅速開(kāi)始支持它。IE9+、Firefox 1.5+、Safari 2+、Opera 9+、Chrome、iOS 版 Safari 以及 Android 版 WebKit 都在某種程度上支持<canvas>。

??與瀏覽器環(huán)境中的其他組件類(lèi)似,<canvas>由幾組 API 構(gòu)成,但并非所有瀏覽器都支持所有這些 API。除了具備基本繪圖能力的 2D 上下文,<canvas>還建議了一個(gè)名為 WebGL 的 3D 上下文。

??目前,支持該元素的瀏覽器都支持 2D 上下文及文本 API,但對(duì) WebGL 的支持還不夠好。由于 WebGL 還是實(shí)驗(yàn)性的,因此要得到所有瀏覽器支持還需要很長(zhǎng)一段時(shí)間。Firefox 4+和 Chrome 支持 WebGL 規(guī)范的早期版本,但一些老版本的操作系統(tǒng),比如 Windows XP,由于缺少必要的繪圖驅(qū)動(dòng)程序,即便安裝了這兩款瀏覽器也無(wú)濟(jì)于事。

1、基本用法

??要使用 <canvas> 元素,必須先設(shè)置 width 和 height 屬性,指定可以繪圖的區(qū)域大小。出現(xiàn)在開(kāi)始和結(jié)束標(biāo)簽中的內(nèi)容是后備信息,如果瀏覽器不支持 <canvas> 元素,就會(huì)顯示這些信息。下面就是 <canvas> 元素的例子。

<canvas id="drawing" width="200" height="200">A drawing of something.</canvas>

??與其它元素一樣,<canvas> 元素對(duì)應(yīng)的 DOM 元素對(duì)象也有 width 和 height 屬性,可以隨意修改。而且,也能通過(guò) CSS 為該元素添加樣式,如果不添加任何樣式或者不繪制任何圖形,在頁(yè)面中是看不到該元素的。
??要在這塊畫(huà)布(canvas)上繪圖,需要取得繪圖上下文。而取得繪圖上下文對(duì)象的引用,需要調(diào)用 getContext() 方法并傳入上下文的名字。傳入"2d",就可以取得 2D 上下文對(duì)象。

var drawing = document.getElementById('drawing');

// 確定瀏覽器支持<canvas>元素
if (drawing.getContext) {
    var context = drawing.getContext("2d");
    // 更多代碼
}

??在使用 <canvas> 元素之前,首先要檢測(cè) getContext() 方法是否存在,這一步非常重要。有些瀏覽器會(huì)為 HTML 規(guī)范之外的元素創(chuàng)建默認(rèn)的 HTML 元素對(duì)象(假設(shè)你想在 Firefox3 中使用 <canvas> 元素,雖然瀏覽器會(huì)為該標(biāo)簽創(chuàng)建一個(gè) DOM 對(duì)象,但這個(gè)對(duì)象中并沒(méi)有 getContext() 方法。)。在這種情況下,即使 drawing 變量中保存著一個(gè)有效的元素引用,也檢測(cè)不到 getContext() 方法。
??使用 toDataURL() 方法,可以導(dǎo)出在<canvas>元素上繪制的圖像。這個(gè)方法接受一個(gè)參數(shù),即圖像的 MIME 類(lèi)型格式,而且適合用于創(chuàng)建圖像的任何上下文。比如,要取得畫(huà)布中的一幅 PNG 格式的圖像,可以使用以下代碼。

var drawing = document.getElementById("drawing");

// 確定瀏覽器支持<canvas>元素
if (drawing.getContext()) {
    // 取得圖像的數(shù)據(jù) URI
    var imgURI = drawing.toDataURL("image/png");

    // 顯示圖像
    var image = document.creatElement("img");
    image.src = imgURI;
    document.body.appendChild(image);
}

??默認(rèn)情況下,瀏覽器會(huì)將圖像編碼為 PNG 格式(除非另行指定)。Firefox 和 Opera 也支持基于 "image/jpeg" 參數(shù)的 JPEG 編碼格式。由于這個(gè)方法是后來(lái)才追加的,所以支持<canvas>的瀏覽器也是在較新的版本中才加入了對(duì)它的支持,比如 IE9、Firefox3.5 和 Opera10。

如果繪制到畫(huà)布上的圖像源自不同的域,toDataURL() 方法會(huì)拋出錯(cuò)誤。

2、2D 上下文

??使用 2D 繪圖上下文提供的方法,可以繪制簡(jiǎn)單的 2D 圖形,比如矩形、弧線和路徑。
??2D 上下文的坐標(biāo)開(kāi)始于<canvas>元素的左上角,原點(diǎn)坐標(biāo)是(0,0)。所有坐標(biāo)值都基于這個(gè)原點(diǎn)計(jì)算,x 值越大表示越靠右,y 值越大表示越靠下。默認(rèn)情況下,width 和 height 表示水平和垂直兩個(gè)方向上可用的像素?cái)?shù)目。

2.1、 填充和描邊

??2D 上下文的兩種基本繪圖操作是填充和描邊。

??填充,就是用指定的樣式(顏色、漸變或圖像)填充圖形;

??描邊,就是只在圖形的邊緣畫(huà)線。

??大多數(shù) 2D 上下文操作都會(huì)細(xì)分為填充和描邊兩個(gè)操作,而操作的結(jié)果取決于兩個(gè)屬性:fillStyle 和 strokeStyle。這兩個(gè)屬性的值可以是字符串、漸變對(duì)象或模式對(duì)象,而且它們的默認(rèn)值是"#000000"。
??如果為它們指定表示顏色的字符串值,可以使用 CSS 中指定顏色值的任何格式,包括顏色名、十六進(jìn)制碼、rgb、rgba、hsl 或 hsla。示例:

var drawing = document.getElementById("drawing");

// 確定瀏覽器支持<canvas>元素
if (drawing.getContext){
    var context = drawing.getContext("2d");
    context.strokeStyle = "red";
    context.fillStyle = "#0000ff";
}

??以上代碼將 strokeStyle 設(shè)置為 red(CSS 中的顏色名),將 fillStyle 設(shè)置為#0000ff(藍(lán)色)。
??然后,所有涉及描邊和填充的操作都將使用這兩個(gè)樣式,直至重新設(shè)置這兩個(gè)值。如前所述,這兩個(gè)屬性的值也可以是漸變對(duì)象或模式對(duì)象。本章后面會(huì)討論這兩種對(duì)象。

2.2、 繪制矩形

??矩形是唯一一種可以直接在 2D 上下文中繪制的形狀。與矩形有關(guān)的方法包括 fillRect()、strokeRect() 和 clearRect()。
??這三個(gè)方法都能接收 4 個(gè)參數(shù):矩形的 x 坐標(biāo)、矩形的 y 坐標(biāo)、矩形寬度和矩形高度。這些參數(shù)的單位都是像素。

??首先,fillRect() 方法在畫(huà)布上繪制的矩形會(huì)填充指定的顏色。填充的顏色通過(guò) fillStyle 屬性指定,比如:

var drawing = document.getElementById("drawing");

// 確定瀏覽器支持<canvas>元素
if (drawing.getContext){
    var context = drawing.getContext("2d");

    // 繪制紅色矩形
    context.fillStyle = "#ff0000";
    context.fillRect(10, 10, 50, 50);

    // 繪制半透明的藍(lán)色矩形
    context.fillStyle = "rgba(0, 0, 255, .5)";
    context.fillRect(30, 30, 50, 50);
}

??以上代碼首先將 fillStyle 設(shè)置為紅色,然后從(10, 10)處開(kāi)始繪制矩形,矩形的寬和高均為 50 像素。然后,通過(guò) rgba() 格式再將 fillStyle 設(shè)置為半透明的藍(lán)色,在第一個(gè)矩形上面繪制第二個(gè)矩形。結(jié)果就是可以透過(guò)藍(lán)色的矩形看到紅色的矩形。

??strokeRect() 方法在畫(huà)布上繪制的矩形會(huì)使用指定的顏色描邊。描邊顏色通過(guò) strokeStyle 屬性指定。示例:

var drawing = document.getElementById("drawing");

// 確定瀏覽器支持<canvas>元素
if (drawing.getContext){
    var context = drawing.getContext("2d");

    // 繪制紅色描邊矩形
    context.strokeStyle= "#ff0000";
    context.strokeRect(10, 10, 50, 50);

    // 繪制半透明的藍(lán)色描邊矩形
    context.strokeStyle= "rgba(0, 0, 255, .5)";
    context.strokeRect(30, 30, 50, 50);
}

??以上代碼繪制了兩個(gè)重疊的矩形。不過(guò),這兩個(gè)矩形都只有框線,內(nèi)部并沒(méi)有填充顏色。

??描邊線條的寬度由 lineWidth 屬性控制,該屬性的值可以是任意整數(shù)。
??另外,通過(guò) lineCap 屬性可以控制線條末端的形狀是平頭、圓頭還是方頭("butt"、"round" 或 "square")。
??通過(guò) lineJoin 屬性可以控制線條相交的方式是圓交、斜
交還是斜接("round"、"bevel" 或 "miter")。

??最后,clearRect() 方法用于清除畫(huà)布上的矩形區(qū)域。本質(zhì)上,這個(gè)方法可以把繪制上下文中的某一矩形區(qū)域變透明。通過(guò)繪制形狀然后再清除指定區(qū)域,就可以生成有意思的效果,例如把某個(gè)形狀切掉一塊。示例:

var drawing = document.getElementById("drawing");

// 確定瀏覽器支持<canvas>元素
if (drawing.getContext){
    var context = drawing.getContext("2d");

    // 繪制紅色矩形
    context.fillStyle = "#ff0000";
    context.fillRect(10, 10, 50, 50);

    // 繪制半透明的藍(lán)色矩形
    context.fillStyle = "rgba(0, 0, 255, .5)";
    context.fillRect(30, 30, 50, 50);

   // 在兩個(gè)矩形重疊的地方清除一個(gè)小矩形
    context.clearRect(40, 40, 10, 10); 
}

2.3、繪制路徑

??2D 繪制上下文支持很多在畫(huà)布上繪制路徑的方法。通過(guò)路徑可以創(chuàng)造出復(fù)雜的形狀和線條。

??要繪制路徑,首先必須調(diào)用 beginPath() 方法,表示要開(kāi)始繪制新路徑。然后,再通過(guò)調(diào)用下列方法來(lái)實(shí)際地繪制路徑。

  • arc(x, y, radius, startAngle, endAngle, counterclockwise):以(x, y)為圓心繪制一條弧線,弧線半徑為 radius,起始和結(jié)束角度(用弧度表示)分別為 startAngle 和 endAngle。最后一個(gè)參數(shù)表示 startAngle 和 endAngle 是否按逆時(shí)針?lè)较蛴?jì)算,值為 false 表示按順時(shí)針?lè)较蛴?jì)算。
  • arcTo(x1, y1, x2, y2, radius):從上一點(diǎn)開(kāi)始繪制一條弧線,到(x2, y2)為止,并且以給定的半徑 radius 穿過(guò)(x1, y1)。
  • bezierCurveTo(c1x, c1y, c2x, c2y, x, y):從上一點(diǎn)開(kāi)始繪制一條曲線,到(x, y) 為止,并且以 (c1x, c1y) 和 (c2x, c2y) 為控制點(diǎn)。
  • lineTo(x, y):從上一點(diǎn)開(kāi)始繪制一條直線,到(x, y)為止。
  • moveTo(x, y):將繪圖游標(biāo)移動(dòng)到(x, y),不畫(huà)線。
  • quadraticCurveTo(cx, cy, x, y):從上一點(diǎn)開(kāi)始繪制一條二次曲線,到(x, y)為止,并且以(cx, cy)作為控制點(diǎn)。
  • rect(x, y, width, height):從點(diǎn)(x, y)開(kāi)始繪制一個(gè)矩形,寬度和高度分別由 width 和 height 指定。這個(gè)方法繪制的是矩形路徑,而不是 strokeRect() 和 fillRect() 所繪制的獨(dú)立的形狀。

??創(chuàng)建了路徑后,接下來(lái)有幾種可能的選擇。如果想繪制一條連接到路徑起點(diǎn)的線條,可以調(diào)用 closePath()。

??如果路徑已經(jīng)完成,你想用 fillStyle 填充它,可以調(diào)用 fill() 方法。

??另外,還可以調(diào)用 stroke() 方法對(duì)路徑描邊,描邊使用的是 strokeStyle。

??最后還可以調(diào)用 clip(),這個(gè)方法可以在路徑上創(chuàng)建一個(gè)剪切區(qū)域。

??下面看一個(gè)例子,即繪制一個(gè)不帶數(shù)字的時(shí)鐘表盤(pán)。

var drawing = document.getElementById("drawing");

// 確定瀏覽器支持<canvas>元素
if (drawing.getContext){

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

    // 開(kāi)始路徑
    context.beginPath();

    // 繪制外圓
    context.arc(100, 100, 99, 0, 2 * Math.PI, false);

    // 繪制內(nèi)圓
    context.moveTo(194, 100);
    context.arc(100, 100, 94, 0, 2 * Math.PI, false);

    // 繪制分針
    context.moveTo(100, 100);
    context.lineTo(100, 15);

    // 繪制時(shí)針
    context.moveTo(100, 100);
    context.lineTo(35, 100);

    // 描邊路徑
    context.stroke();
}

??上述例子使用 arc() 方法繪制了兩個(gè)圓形:一個(gè)外圓和一個(gè)內(nèi)圓,構(gòu)成了表盤(pán)的邊框。外圓的半徑是 99 像素,圓心位于點(diǎn)(100,100),也是畫(huà)布的中心點(diǎn)。為了繪制一個(gè)完整的圓形,我們從 0 弧度開(kāi)始,繪制 2π 弧度(通過(guò) Math.PI 來(lái)計(jì)算)。
??在繪制內(nèi)圓之前,必須把路徑移動(dòng)到內(nèi)圓上的某一點(diǎn),以避免繪制出多余的線條。
??第二次調(diào)用 arc() 使用了小一點(diǎn)的半徑,以便創(chuàng)造邊框的效果。
??然后,組合使用 moveTo() 和 lineTo() 方法來(lái)繪制時(shí)針和分針。
??最后一步是調(diào)用 stroke() 方法,這樣才能把圖形繪制到畫(huà)布上,如下圖所示:

??在 2D 繪圖上下文中,路徑是一種主要的繪圖方式,因?yàn)槁窂侥転橐L制的圖形提供更多控制。
??由于路徑的使用很頻繁,所以就有了一個(gè)名為 isPointInPath() 的方法。這個(gè)方法接收 x 和 y 坐標(biāo)作為參數(shù),用于在路徑被關(guān)閉之前確定畫(huà)布上的某一點(diǎn)是否位于路徑上,例如:

if (context.isPointInPath(100, 100)){
    alert("Point (100, 100) is in the path.");
}

??2D 上下文中的路徑 API 已經(jīng)非常穩(wěn)定,可以利用它們結(jié)合不同的填充和描邊樣式,繪制出非常復(fù)雜的圖形來(lái)。

2.4、 繪制文本

??文本與圖形總是如影隨形。為此,2D 繪圖上下文也提供了繪制文本的方法。

??繪制文本主要有兩個(gè)方法:fillText() 和 strokeText()。這兩個(gè)方法都可以接收 4 個(gè)參數(shù):要繪制的文本字符串、x 坐標(biāo)、y 坐標(biāo)和可選的最大像素寬度。而且,這兩個(gè)方法都以下列 3 個(gè)屬性為基礎(chǔ)。

  • font:表示文本樣式、大小及字體,用 CSS 中指定字體的格式來(lái)指定,例如"10px Arial"。
  • textAlign:表示文本對(duì)齊方式??赡艿闹涤?"start"、"end"、"left"、"right" 和 "center"。建議使用"start"和"end",不要使用"left"和"right",因?yàn)榍皟烧叩囊馑几€(wěn)妥,能同時(shí)適合從左到右和從右到左顯示(閱讀)的語(yǔ)言。
  • textBaseline:表示文本的基線??赡艿闹涤?"top"、"hanging"、"middle"、 "alphabetic"、"ideographic" 和 "bottom"。

??這幾個(gè)屬性都有默認(rèn)值,因此沒(méi)有必要每次使用它們都重新設(shè)置一遍值。

??fillText() 方法使用 fillStyle 屬性繪制文本,而 strokeText() 方法使用 strokeStyle 屬性為文本描邊。
??相對(duì)來(lái)說(shuō),還是使用 fillText() 的時(shí)候更多,因?yàn)樵摲椒7铝嗽诰W(wǎng)頁(yè)中正常顯示文本。例如,下面的代碼在前一節(jié)創(chuàng)建的表盤(pán)上方繪制了數(shù)字 12:

context.font = "bold 14px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText("12", 100, 20); 

??因?yàn)檫@里把 textAlign 設(shè)置為"center",把 textBaseline 設(shè)置為"middle",所以坐標(biāo)(100, 20) 表示的是文本水平和垂直中點(diǎn)的坐標(biāo)。
??如果將 textAlign 設(shè)置為"start",則 x 坐標(biāo)表示的是文本左端的位置(從左到右閱讀的語(yǔ)言);設(shè)置為"end",則 x 坐標(biāo)表示的是文本右端的位置(從左到右閱讀的
語(yǔ)言)。例如:

// 正常
context.font = "bold 14px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText("12", 100, 20);

// 起點(diǎn)對(duì)齊
context.textAlign = "start";
context.fillText("12", 100, 40);

// 終點(diǎn)對(duì)齊
context.textAlign = "end";
context.fillText("12", 100, 60);

??這一回繪制了三個(gè)字符串"12",每個(gè)字符串的 x 坐標(biāo)值相同,但 textAlign 值不同。另外,后兩個(gè)字符串的 y 坐標(biāo)依次增大,以避免相互重疊。結(jié)果如下圖所示:

??表盤(pán)中的分針恰好位于正中間,因此文本的水平對(duì)齊方式如何變化也能夠一目了然。

??類(lèi)似地,修改 textBaseline 屬性的值可以調(diào)整文本的垂直對(duì)齊方式:值為"top",y 坐標(biāo)表示文本頂端;值為 "bottom",y 坐標(biāo)表示文本底端;值為 "hanging"、"alphabetic" 和 "ideographic",則 y 坐標(biāo)分別指向字體的特定基線坐標(biāo)。

??由于繪制文本比較復(fù)雜,特別是需要把文本控制在某一區(qū)域中的時(shí)候,2D 上下文提供了輔助確定文本大小的方法 measureText()。
??這個(gè)方法接收一個(gè)參數(shù),即要繪制的文本;返回一個(gè) TextMetrics 對(duì)象。返回的對(duì)象目前只有一個(gè) width 屬性,但將來(lái)還會(huì)增加更多度量屬性。

??measureText() 方法利用 font、textAlign 和 textBaseline 的當(dāng)前值計(jì)算指定文本的大小。
??比如,假設(shè)你想在一個(gè) 140 像素寬的矩形區(qū)域中繪制文本 Hello world!,下面的代碼從 100 像素的字體大小開(kāi)始遞減,最終會(huì)找到合適的字體大小。

var fontSize = 100;
context.font = fontSize + "px Arial";
while(context.measureText("Hello world!").width > 140){
    fontSize--;
    context.font = fontSize + "px Arial";
}
context.fillText("Hello world!", 10, 10);
context.fillText("Font size is " + fontSize + "px", 10, 50); 

??前面提到過(guò),fillText 和 strokeText() 方法都可以接收第四個(gè)參數(shù),也就是文本的最大像素寬度。不過(guò),這個(gè)可選的參數(shù)尚未得到所有瀏覽器支持(最早支持它的是 Firefox 4)。
??提供這個(gè)參數(shù)后,調(diào)用 fillText() 或 strokeText() 時(shí)如果傳入的字符串大于最大寬度,則繪制的文本字符的高度正確,但寬度會(huì)收縮以適應(yīng)最大寬度。
??繪制文本還是相對(duì)比較復(fù)雜的操作,因此支持<canvas>元素的瀏覽器也并未完全實(shí)現(xiàn)所有與繪制文本相關(guān)的 API。

2.5、變換

??通過(guò)上下文的變換,可以把處理后的圖像繪制到畫(huà)布上。2D 繪制上下文支持各種基本的繪制變換。
??創(chuàng)建繪制上下文時(shí),會(huì)以默認(rèn)值初始化變換矩陣,在默認(rèn)的變換矩陣下,所有處理都按描述直接繪制。
??為繪制上下文應(yīng)用變換,會(huì)導(dǎo)致使用不同的變換矩陣應(yīng)用處理,從而產(chǎn)生不同的結(jié)果。
??可以通過(guò)如下方法來(lái)修改變換矩陣。

  • rotate(angle):圍繞原點(diǎn)旋轉(zhuǎn)圖像 angle 弧度。
  • scale(scaleX, scaleY):縮放圖像,在 x 方向乘以 scaleX,在 y 方向乘以 scaleY。scaleX 和 scaleY 的默認(rèn)值都是 1.0。
  • translate(x, y):將坐標(biāo)原點(diǎn)移動(dòng)到(x,y)。執(zhí)行這個(gè)變換之后,坐標(biāo)(0,0)會(huì)變成之前由(x, y)表示的點(diǎn)。
  • transform(m1_1, m1_2, m2_1, m2_2, dx, dy):直接修改變換矩陣,方式是乘以如下矩陣。
    m1_1??m1_2??dx
    m2_1??m2_2??dy
    0????0????1
  • setTransform(m1_1, m1_2, m2_1, m2_2, dx, dy):將變換矩陣重置為默認(rèn)狀態(tài),然后再調(diào)用 transform()。

??變換有可能很簡(jiǎn)單,但也可能很復(fù)雜,這都要視情況而定。比如,就拿前面例子中繪制表針來(lái)說(shuō),如果把原點(diǎn)變換到表盤(pán)的中心,然后再繪制表針就容易多了。請(qǐng)看下面的例子。

var drawing = document.getElementById("drawing");

// 確定瀏覽器支持<canvas>元素
if (drawing.getContext){ 

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

    // 開(kāi)始路徑
    context.beginPath();

    // 繪制外圓
    context.arc(100, 100, 99, 0, 2 * Math.PI, false);

    // 繪制內(nèi)圓
    context.moveTo(194, 100);
    context.arc(100, 100, 94, 0, 2 * Math.PI, false);

    // 變換原點(diǎn)
    context.translate(100, 100);

    // 繪制分針
    context.moveTo(0,0);
    context.lineTo(0, -85);

    // 繪制時(shí)針
    context.moveTo(0, 0);
    context.lineTo(-65, 0);

    // 描邊路徑
    context.stroke();
}

??把原點(diǎn)變換到時(shí)鐘表盤(pán)的中心點(diǎn)(100, 100)后,在同一方向上繪制線條就變成了簡(jiǎn)單的數(shù)學(xué)問(wèn)題了。所有數(shù)學(xué)計(jì)算都基于(0, 0),而不是(100, 100)。
??還可以更進(jìn)一步,像下面這樣使用 rotate() 方法旋轉(zhuǎn)時(shí)鐘的表針。

var drawing = document.getElementById("drawing");

// 確定瀏覽器支持<canvas>元素
if (drawing.getContext){ 

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

    // 開(kāi)始路徑
    context.beginPath();

    // 繪制外圓
    context.arc(100, 100, 99, 0, 2 * Math.PI, false);

    // 繪制內(nèi)圓
    context.moveTo(194, 100);
    context.arc(100, 100, 94, 0, 2 * Math.PI, false);

    // 變換原點(diǎn)
    context.translate(100, 100);

     // 旋轉(zhuǎn)表針
     context.rotate(1);

    // 繪制分針
    context.moveTo(0,0);
    context.lineTo(0, -85);

    // 繪制時(shí)針
    context.moveTo(0, 0);
    context.lineTo(-65, 0);

    // 描邊路徑
    context.stroke();
}

??因?yàn)樵c(diǎn)已經(jīng)變換到了時(shí)鐘表盤(pán)的中心點(diǎn),所以旋轉(zhuǎn)也是以該點(diǎn)為圓心的。結(jié)果就像是表針真地被固定在表盤(pán)中心一樣,然后向右旋轉(zhuǎn)了一定角度。

??無(wú)論是剛才執(zhí)行的變換,還是 fillStyle、strokeStyle 等屬性,都會(huì)在當(dāng)前上下文中一直有效,除非再對(duì)上下文進(jìn)行什么修改。
??雖然沒(méi)有什么辦法把上下文中的一切都重置回默認(rèn)值,但有兩個(gè)方法可以跟蹤上下文的狀態(tài)變化。如果你知道將來(lái)還要返回某組屬性與變換的組合,可以調(diào)用 save() 方法。
??調(diào)用這個(gè)方法后,當(dāng)時(shí)的所有設(shè)置都會(huì)進(jìn)入一個(gè)棧結(jié)構(gòu),得以妥善保管。然后可以對(duì)上下文進(jìn)行其他修改。等想要回到之前保存的設(shè)置時(shí),可以調(diào)用 restore() 方法,在保存設(shè)置的棧結(jié)構(gòu)中向前返回一級(jí),
恢復(fù)之前的狀態(tài)。
??連續(xù)調(diào)用 save() 可以把更多設(shè)置保存到棧結(jié)構(gòu)中,之后再連續(xù)調(diào)用 restore() 則可以一級(jí)一級(jí)返回。下面來(lái)看一個(gè)例子。

context.fillStyle = "#ff0000";
context.save();

context.fillStyle = "#00ff00";
context.translate(100, 100);
context.save();

context.fillStyle = "#0000ff";
context.fillRect(0, 0, 100, 200); // 從點(diǎn)(100,100)開(kāi)始繪制藍(lán)色矩形

context.restore();
context.fillRect(10, 10, 100, 200); // 從點(diǎn)(110,110)開(kāi)始繪制綠色矩形

context.restore(); 
context.fillRect(0, 0, 100, 200); // 從點(diǎn)(0,0)開(kāi)始繪制紅色矩形

??首先,將 fillStyle 設(shè)置為紅色,并調(diào)用 save() 保存上下文狀態(tài)。
??接下來(lái),把 fillStyle 修改為綠色,把坐標(biāo)原點(diǎn)變換到(100, 100),再調(diào)用 save() 保存上下文狀態(tài)。
??然后,把 fillStyle 修改為藍(lán)色并繪制藍(lán)色的矩形。因?yàn)榇藭r(shí)的坐標(biāo)原點(diǎn)已經(jīng)變了,所以矩形的左上角坐標(biāo)實(shí)際上是(100, 100)。
??然后調(diào)用 restore(),之后 fillStyle 變回了綠色,因而第二個(gè)矩形就是綠色。之所以第二個(gè)矩形的起點(diǎn)坐標(biāo)是(110, 110),是因?yàn)樽鴺?biāo)位置的變換仍然起作用。
??再調(diào)用一次 restore(),變換就被取消了,而 fillStyle 也返回了紅色。所以最后一個(gè)矩形是紅色的,而且繪制的起點(diǎn)是(0,0)。
??需要注意的是,save() 方法保存的只是對(duì)繪圖上下文的設(shè)置和變換,不會(huì)保存繪圖上下文的內(nèi)容。

2.6、 繪制圖像

?? 繪圖上下文內(nèi)置了對(duì)圖像的支持。如果你想把一幅圖像繪制到畫(huà)布上,可以使用 drawImage() 方法。根據(jù)期望的最終結(jié)果不同,調(diào)用這個(gè)方法時(shí),可以使用三種不同的參數(shù)組合。最簡(jiǎn)單的調(diào)用方式是傳入一個(gè) HTML <img> 元素,以及繪制該圖像的起點(diǎn)的 x 和 y 坐標(biāo)。例如:

var image = document.images[0];
context.drawImage(image, 10, 10);

??這兩行代碼取得了文檔中的第一幅圖像,然后將它繪制到上下文中,起點(diǎn)為(10, 10)。繪制到畫(huà)布上的圖像大小與原始大小一樣。
??如果你想改變繪制后圖像的大小,可以再多傳入兩個(gè)參數(shù),分別表示目標(biāo)寬度和目標(biāo)高度。通過(guò)這種方式來(lái)縮放圖像并不影響上下文的變換矩陣。例如:

context.drawImage(image, 50, 10, 20, 30);

??執(zhí)行代碼后,繪制出來(lái)的圖像大小會(huì)變成 20×30 像素。

??除了上述兩種方式,還可以選擇把圖像中的某個(gè)區(qū)域繪制到上下文中。

??drawImage() 方法的這種調(diào)用方式總共需要傳入 9 個(gè)參數(shù):要繪制的圖像、源圖像的 x 坐標(biāo)、源圖像的 y 坐標(biāo)、源圖像的寬度、源圖像的高度、目標(biāo)圖像的 x 坐標(biāo)、目標(biāo)圖像的 y 坐標(biāo)、目標(biāo)圖像的寬度、目標(biāo)圖像的高度。這樣調(diào)用 drawImage() 方法可以獲得最多的控制。例如:

context.drawImage(image, 0, 10, 50, 50, 0, 100, 40, 60); 

??這行代碼只會(huì)把原始圖像的一部分繪制到畫(huà)布上。原始圖像的這一部分的起點(diǎn)為(0, 10),寬和高都是 50 像素。最終繪制到上下文中的圖像的起點(diǎn)是(0, 100),而大小變成了 40×60 像素。
??這種調(diào)用方式可以創(chuàng)造出很有意思的效果,如下圖所示:

??除了給 drawImage() 方法傳入 HTML <img> 元素外,還可以傳入另一個(gè)<canvas>元素作為其第一個(gè)參數(shù)。這樣,就可以把另一個(gè)畫(huà)布內(nèi)容繪制到當(dāng)前畫(huà)布上。
??結(jié)合使用 drawImage() 和其他方法,可以對(duì)圖像進(jìn)行各種基本操作。而操作的結(jié)果可以通過(guò) toDataURL() 方法獲得。(請(qǐng)讀者注意,雖然本章至今一直在討論 2D 繪圖上下文,但 toDataURL()是 Canvas 對(duì)象的方法,不是上下文對(duì)象的方法。)
??不過(guò),有一個(gè)例外,即圖像不能來(lái)自其他域。如果圖像來(lái)自其他域,調(diào)用 toDataURL() 會(huì)拋出一個(gè)錯(cuò)誤。打個(gè)比方,假如位于www.example.com 上的頁(yè)面繪制的圖像來(lái)自于 www.wrox.com,那當(dāng)前上下文就會(huì)被認(rèn)為“不干凈”,因而會(huì)拋出錯(cuò)誤。

2.7、陰影

??2D 上下文會(huì)根據(jù)以下幾個(gè)屬性的值,自動(dòng)為形狀或路徑繪制出陰影。

  • shadowColor:用 CSS 顏色格式表示的陰影顏色,默認(rèn)為黑色。
  • shadowOffsetX:形狀或路徑 x 軸方向的陰影偏移量,默認(rèn)為 0。
  • shadowOffsetY:形狀或路徑 y 軸方向的陰影偏移量,默認(rèn)為 0。
  • shadowBlur:模糊的像素?cái)?shù),默認(rèn) 0,即不模糊。

??這些屬性都可以通過(guò) context 對(duì)象來(lái)修改。只要在繪制前為它們?cè)O(shè)置適當(dāng)?shù)闹担湍茏詣?dòng)產(chǎn)生陰影。例如:

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

// 設(shè)置陰影
context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 4;
context.shadowColor = "rgba(0, 0, 0, 0.5)";

// 繪制紅色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);

// 繪制藍(lán)色矩形
context.fillStyle = "rgba(0,0,255,1)"; 
context.fillRect(30, 30, 50, 50); 

??兩個(gè)矩形的陰影樣式相同,如下圖所示:

??不同瀏覽器對(duì)陰影的支持有一些差異。IE9、Firefox 4 和 Opera 11 的行為最為規(guī)范,其他瀏覽器多多少少會(huì)有一些奇怪的現(xiàn)象,甚至根本不支持陰影。
??Chrome(直至第 10 版)不能正確地為描邊的形狀應(yīng)用實(shí)心陰影。Chrome 和 Safari(直至第 5 版)在為帶透明像素的圖像應(yīng)用陰影時(shí)也會(huì)有問(wèn)題:不透明部分的下方本來(lái)是該有陰影的,但此時(shí)則一概不見(jiàn)了。Safari 也不能給漸變圖形應(yīng)用陰影,其他瀏覽器都可以。

2.8、漸變

??漸變由 CanvasGradient 實(shí)例表示,很容易通過(guò) 2D 上下文來(lái)創(chuàng)建和修改。要?jiǎng)?chuàng)建一個(gè)新的線性漸變,可以調(diào)用 createLinearGradient() 方法。這個(gè)方法接收 4 個(gè)參數(shù):起點(diǎn)的 x 坐標(biāo)、起點(diǎn)的 y 坐標(biāo)、終點(diǎn)的 x 坐標(biāo)、終點(diǎn)的 y 坐標(biāo)。
??調(diào)用這個(gè)方法后,它就會(huì)創(chuàng)建一個(gè)指定大小的漸變,并返回 CanvasGradient 對(duì)象的實(shí)例。
??創(chuàng)建了漸變對(duì)象后,下一步就是使用 addColorStop() 方法來(lái)指定色標(biāo)。這個(gè)方法接收兩個(gè)參數(shù):色標(biāo)位置和 CSS 顏色值。色標(biāo)位置是一個(gè) 0(開(kāi)始的顏色)到 1(結(jié)束的顏色)之間的數(shù)字。例如:

var gradient = context.createLinearGradient(30, 30, 70, 70);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");

??此時(shí),gradient 對(duì)象表示的是一個(gè)從畫(huà)布上點(diǎn)(30, 30)到點(diǎn)(70, 70)的漸變。起點(diǎn)的色標(biāo)是白色,終點(diǎn)的色標(biāo)是黑色。然后就可以把 fillStyle 或 strokeStyle 設(shè)置為這個(gè)對(duì)象,從而使用漸變來(lái)繪制形狀或描邊:

// 繪制紅色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);

// 繪制漸變矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);

??為了讓漸變覆蓋整個(gè)矩形,而不是僅應(yīng)用到矩形的一部分,矩形和漸變對(duì)象的坐標(biāo)必須匹配才行。以上代碼會(huì)得到如下圖所示的結(jié)果:

??如果沒(méi)有把矩形繪制到恰當(dāng)?shù)奈恢?,那可能就只?huì)顯示部分漸變效果。例如:

context.fillStyle = gradient; 
context.fillRect(50, 50, 50, 50);

??這兩行代碼執(zhí)行后得到的矩形只有左上角稍微有一點(diǎn)白色。這主要是因?yàn)榫匦蔚钠瘘c(diǎn)位于漸變的中間位置,而此時(shí)漸變差不多已經(jīng)結(jié)束了。由于漸變不重復(fù),所以矩形的大部分區(qū)域都是黑色。如下圖所示:


??確保漸變與形狀對(duì)齊非常重要,有時(shí)候可以考慮使用函數(shù)來(lái)確保坐標(biāo)合適。例如:

function createRectLinearGradient(context, x, y, width, height){
    return context.createLinearGradient(x, y, x + width, y + height);
}

??這個(gè)函數(shù)基于起點(diǎn)的 x 和 y 坐標(biāo)以及寬度和高度值來(lái)創(chuàng)建漸變對(duì)象,從而讓我們可以在 fillRect() 中使用相同的值。

var gradient = createRectLinearGradient(context, 30, 30, 50, 50);

gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");

//繪制漸變矩形
context.fi llStyle = gradient;
context.fillRect(30, 30, 50, 50);

??使用畫(huà)布的時(shí)候,確保坐標(biāo)匹配很重要,也需要一些技巧。類(lèi)似 createRectLinearGradient() 這樣的輔助方法可以讓控制坐標(biāo)更容易一些。

??要?jiǎng)?chuàng)建徑向漸變(或放射漸變),可以使用 createRadialGradient() 方法。這個(gè)方法接收 6 個(gè)參數(shù),對(duì)應(yīng)著兩個(gè)圓的圓心和半徑。前三個(gè)參數(shù)指定的是起點(diǎn)圓的原心(x 和 y)及半徑,后三個(gè)參數(shù)指定的是終點(diǎn)圓的原心(x 和 y)及半徑。
??可以把徑向漸變想象成一個(gè)長(zhǎng)圓桶,而這 6 個(gè)參數(shù)定義的正是這個(gè)桶的兩個(gè)圓形開(kāi)口的位置。如果把一個(gè)圓形開(kāi)口定義得比另一個(gè)小一些,那這個(gè)圓桶就變成了圓錐體,而通過(guò)移動(dòng)每個(gè)圓形開(kāi)口的位置,就可達(dá)到像旋轉(zhuǎn)這個(gè)圓錐體一樣的效果。
??如果想從某個(gè)形狀的中心點(diǎn)開(kāi)始創(chuàng)建一個(gè)向外擴(kuò)散的徑向漸變效果,就要將兩個(gè)圓定義為同心圓。
??比如,就拿前面創(chuàng)建的矩形來(lái)說(shuō),徑向漸變的兩個(gè)圓的圓心都應(yīng)該在(55, 55),因?yàn)榫匦蔚膮^(qū)域是從(30, 30)到(80,80)。請(qǐng)看代碼:

var gradient = context.createRadialGradient(55, 55, 10, 55, 55, 30);

gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");

//繪制紅色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);

//繪制漸變矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50); 

??運(yùn)行代碼,會(huì)得到如下圖所示的結(jié)果。

??因?yàn)閯?chuàng)建比較麻煩,所以徑向漸變并不那么容易控制。不過(guò),一般來(lái)說(shuō),讓起點(diǎn)圓和終點(diǎn)圓保持為同心圓的情況比較多,這時(shí)候只要考慮給兩個(gè)圓設(shè)置不同的半徑就好了。

2.9、模式

??模式其實(shí)就是重復(fù)的圖像,可以用來(lái)填充或描邊圖形。要?jiǎng)?chuàng)建一個(gè)新模式,可以調(diào)用 createPattern() 方法并傳入兩個(gè)參數(shù):一個(gè) HTML <img> 元素和一個(gè)表示如何重復(fù)圖像的字符串。
??其中,第二個(gè)參數(shù)的值與 CSS 的background-repeat 屬性值相同,包括"repeat"、"repeat-x"、"repeat-y" 和 "no-repeat"。示例:

var image = document.images[0],
    pattern = context.createPattern(image, "repeat");

// 繪制矩形
context.fillStyle = pattern;
context.fillRect(10, 10, 150, 150); 

??需要注意的是,模式與漸變一樣,都是從畫(huà)布的原點(diǎn)(0, 0)開(kāi)始的。
??將填充樣式(fillStyle)設(shè)置為模式對(duì)象,只表示在某個(gè)特定的區(qū)域內(nèi)顯示重復(fù)的圖像,而不是要從某個(gè)位置開(kāi)始繪制重復(fù)的圖像。
??createPattern() 方法的第一個(gè)參數(shù)也可以是一個(gè)<video>元素,或者另一個(gè)<canvas>元素。

2.10、使用圖像數(shù)據(jù)

??2D 上下文的一個(gè)明顯的長(zhǎng)處就是,可以通過(guò) getImageData() 取得原始圖像數(shù)據(jù)。
??這個(gè)方法接收 4 個(gè)參數(shù):要取得其數(shù)據(jù)的畫(huà)面區(qū)域的 x 和 y 坐標(biāo)以及該區(qū)域的像素寬度和高度。
??示例,要取得左上角坐標(biāo)為(10, 5)、大小為 50x50 像素的區(qū)域的圖像數(shù)據(jù),可以使用以下代碼:

var imageData = context.getImageData(10, 5, 50, 50);

??上述返回的對(duì)象是 ImageData 的實(shí)例。
??每個(gè) ImageData 對(duì)象都有三個(gè)屬性:width、height 和 data。
??其中 data 屬性是一個(gè)數(shù)組,保存著圖像中每一個(gè)像素的數(shù)據(jù)。在 data 數(shù)組中,每一個(gè)像素用 4 個(gè)元素來(lái)保存,分別表示紅、綠、藍(lán)和透明度。因此,第一個(gè)像素的數(shù)據(jù)就保存在數(shù)組的第 0 到第 3 個(gè)元素中,示例:

var data = imageData,
    red = data[0],
    green = data[1],
    blue = data[2],
    alpha = data[3];

??數(shù)組中的每個(gè)元素的值都介于 0 到 255 之間(包括 0 和 255)。能夠直接訪問(wèn)到原始圖像數(shù)據(jù),就能夠以各種方式來(lái)操作這些數(shù)據(jù)。
??例如,通過(guò)修改圖像數(shù)據(jù),可以像下面這樣創(chuàng)建一個(gè)簡(jiǎn)單的灰階過(guò)濾器。

var drawing = document.getElementById("drawing");

// 確定瀏覽器支持<canvas>元素
if (drawing.getContext){
    var context = drawing.getContext("2d"),
        image = document.images[0],
        imageData, data,
        i, len, average,
        red, green, blue, alpha;

    // 繪制原始圖像
    context.drawImage(image, 0, 0);

    // 取得圖像數(shù)據(jù)
    imageData = context.getImageData(0, 0, image.width, image.height);
    data = imageData.data;

    for (i=0, len=data.length; i < len; i+=4){
        red = data[i];
        green = data[i+1];
        blue = data[i+2];
        alpha = data[i+3];

        // 求得 rgb 平均值
        average = Math.floor((red + green + blue) / 3);

        // 設(shè)置顏色值,透明度不變
        data[i] = average;
        data[i+1] = average;
        data[i+2] = average;
    }

    // 回寫(xiě)圖像數(shù)據(jù)并顯示結(jié)果
    imageData.data = data;
    context.putImageData(imageData, 0, 0);
} 

??上述例子首先在畫(huà)面上繪制了一幅圖像,然后取得了原始圖像數(shù)據(jù)。其中的 for 循環(huán)遍歷了圖像數(shù)據(jù)中的每一個(gè)像素。這里要注意的是,每次循環(huán)控制變量 i 都遞增 4。
??在取得每個(gè)像素的紅、綠、藍(lán)顏色值后,計(jì)算出它們的平均值。再把這個(gè)平均值設(shè)置為每個(gè)顏色的值,結(jié)果就是去掉了每個(gè)像素的顏色,只保留了亮度接近的灰度值(即彩色變黑白)。
??在把 data 數(shù)組回寫(xiě)到 imageData 對(duì)象后,調(diào)用putImageData() 方法把圖像數(shù)據(jù)繪制到畫(huà)布上。最終得到了圖像的黑白版。
??當(dāng)然,通過(guò)操作原始像素值不僅能實(shí)現(xiàn)灰階過(guò)濾,還能實(shí)現(xiàn)其他功能。

??只有在畫(huà)布“干凈”的情況下(即圖像并非來(lái)自其他域),才可以取得圖像數(shù)據(jù)。如果畫(huà)布“不干凈”,那么訪問(wèn)圖像數(shù)據(jù)時(shí)會(huì)導(dǎo)致 JavaScript 錯(cuò)誤。

2.11、合成

??還有兩個(gè)會(huì)應(yīng)用到 2D 上下文中所有繪制操作的屬性:globalAlpha 和 globalCompositionOperation。
??其中,globalAlpha 是一個(gè)介于 0 和 1 之間的值(包括 0 和 1),用于指定所有繪制的透明度。默認(rèn)值為 0。如果所有后續(xù)操作都要基于相同的透明度,就可以先把 globalAlpha 設(shè)置為適當(dāng)值,然后繪制,最后再把它設(shè)置回默認(rèn)值 0。示例:

// 繪制紅色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);

// 修改全局透明度
context.globalAlpha = 0.5;

// 繪制藍(lán)色矩形
context.fillStyle = "rgba(0, 0, 255, 1)";
context.fillRect(30, 30, 50, 50);

// 重置全局透明度
context.globalAlpha = 0; 

??在上述例子中,我們把藍(lán)色矩形繪制到了紅色矩形上面。因?yàn)樵诶L制藍(lán)色矩形前,globalAlpha 已經(jīng)被設(shè)置為 0.5,所以藍(lán)色矩形會(huì)呈現(xiàn)半透明效果,透過(guò)它可以看到下面的紅色矩形。

??第二個(gè)屬性 globalCompositionOperation 表示后繪制的圖形怎樣與先繪制的圖形結(jié)合。這個(gè)屬性的值是字符串,可能的值如下。

  • source-over(默認(rèn)值):后繪制的圖形位于先繪制的圖形上方。
  • source-in:后繪制的圖形與先繪制的圖形重疊的部分可見(jiàn),兩者其他部分完全透明。
  • source-out:后繪制的圖形與先繪制的圖形不重疊的部分可見(jiàn),先繪制的圖形完全透明。
  • source-atop:后繪制的圖形與先繪制的圖形重疊的部分可見(jiàn),先繪制圖形不受影響。
  • destination-over:后繪制的圖形位于先繪制的圖形下方,只有之前透明像素下的部分才可見(jiàn)。
  • destination-in:后繪制的圖形位于先繪制的圖形下方,兩者不重疊的部分完全透明。
  • destination-out:后繪制的圖形擦除與先繪制的圖形重疊的部分。
  • destination-atop:后繪制的圖形位于先繪制的圖形下方,在兩者不重疊的地方,先繪制的圖形會(huì)變透明。
  • lighter:后繪制的圖形與先繪制的圖形重疊部分的值相加,使該部分變亮。
  • copy:后繪制的圖形完全替代與之重疊的先繪制圖形。
  • xor:后繪制的圖形與先繪制的圖形重疊的部分執(zhí)行“異或”操作。

??這個(gè)合成操作實(shí)際上用語(yǔ)言或者黑白圖像是很難說(shuō)清楚的。要了解每個(gè)操作的具體效果,請(qǐng)參見(jiàn) https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html。推薦使用 IE9+或 Firefox 4+訪問(wèn)前面的網(wǎng)頁(yè),因?yàn)檫@兩款瀏覽器對(duì) Canvas 的實(shí)現(xiàn)最完善。下面來(lái)看一個(gè)例子。

// 繪制紅色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);

// 設(shè)置合成操作
context.globalCompositeOperation = "destination-over";

// 繪制藍(lán)色矩形
context.fillStyle = "rgba(0,0,255,1)";
context.fillRect(30, 30, 50, 50);

??如果不修改 globalCompositionOperation,那么藍(lán)色矩形應(yīng)該位于紅色矩形之上。但把 globalCompositionOperation 設(shè)置為"destination-over"之后,紅色矩形跑到了藍(lán)色矩形上面。
??在使用 globalCompositionOperation 的情況下,一定要多測(cè)試一些瀏覽器。因?yàn)椴煌瑸g覽器對(duì)這個(gè)屬性的實(shí)現(xiàn)仍然存在較大的差別。Safari 和 Chrome 在這方面還有問(wèn)題,至于有什么問(wèn)題,大家可以比較在打開(kāi)上述頁(yè)面的情況下,IE9+和 Firefox 4+與它們有什么差異。

小結(jié)

??HTML5 的<canvas>元素提供了一組 JavaScript API,讓我們可以動(dòng)態(tài)地創(chuàng)建圖形和圖像。圖形是在一個(gè)特定的上下文中創(chuàng)建的,而上下文對(duì)象目前有兩種。第一種是 2D 上下文,可以執(zhí)行原始的繪圖操作,比如:

  • 設(shè)置填充、描邊顏色和模式
  • 繪制矩形
  • 繪制路徑
  • 繪制文本
  • 創(chuàng)建漸變和模式

??第二種是 3D 上下文,即 WebGL 上下文。WebGL 是從 OpenGL ES 2.0 移植到瀏覽器中的,而 OpenGLES 2.0 是游戲開(kāi)發(fā)人員在創(chuàng)建計(jì)算機(jī)圖形圖像時(shí)經(jīng)常使用的一種語(yǔ)言。WebGL 支持比 2D 上下文更豐富和更強(qiáng)大的圖形圖像處理能力,比如:

  • 用 GLSL(OpenGL Shading Language,OpenGL 著色語(yǔ)言)編寫(xiě)的頂點(diǎn)和片段著色器
  • 支持類(lèi)型化數(shù)組,即能夠?qū)?shù)組中的數(shù)據(jù)限定為某種特定的數(shù)值類(lèi)型
  • 創(chuàng)建和操作紋理

??目前,主流瀏覽器的較新版本大都已經(jīng)支持<canvas>標(biāo)簽。同樣地,這些版本的瀏覽器基本上也都支持 2D 上下文。但對(duì)于 WebGL 而言,目前還只有 Firefox 4+和 Chrome 支持它。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本章內(nèi)容 理解 元素 繪制簡(jiǎn)單的 2D 圖形 使用 WebGL 繪制 3D 圖形 這個(gè)元素負(fù)責(zé)在頁(yè)面中設(shè)定一個(gè)區(qū)域...
    悶油瓶小張閱讀 986評(píng)論 0 0
  • --繪圖與濾鏡全面解析 概述 在iOS中可以很容易的開(kāi)發(fā)出絢麗的界面效果,一方面得益于成功系統(tǒng)的設(shè)計(jì),另一方面得益...
    韓七夏閱讀 2,975評(píng)論 2 10
  • Core Graphics Framework是一套基于C的API框架,使用了Quartz作為繪圖引擎。它提供了低...
    ShanJiJi閱讀 1,722評(píng)論 0 20
  • 遇見(jiàn)你,我就好像遇見(jiàn)了真正的自己 我說(shuō),你欠我很多擁抱 你說(shuō),我是你不能擁抱的短暫理想 那你就努力伸長(zhǎng)手夠啊 我知...
    洛邀閱讀 559評(píng)論 0 0

友情鏈接更多精彩內(nèi)容