国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
使用 HTML5 canvas 進行 Web 繪圖

使用 HTML5 canvas 進行 Web 繪圖

林 林, 軟件工程師, IBM

簡介: 新的 HTML5 規(guī)范旨在幫助開發(fā)人員更輕松的編寫出各類 Web 應(yīng)用,以順應(yīng)當前 SaaS,云計算以及 RIA 等技術(shù)的最新趨勢。在 HTML5 得以廣泛推廣之前,開發(fā)人員通常使用 SVG,VML 等技術(shù)進行 Web 繪圖操作,但這些基于 XML 的繪圖語言聲明式的繪圖方式并不能滿足復(fù)雜繪圖操作在性能上的需求,比如 Web 游戲所需要的像素級別的繪圖能力。HTML5 canvas 元素的出現(xiàn)填補了這種不足,開發(fā)人員可以使用 JavaScript 腳本語言在 canvas 中進行一系列基于命令的圖形繪制操作,本文將通過講解如何使用 canvas 元素進行基本繪圖操作,以及完成簡單的動畫和用戶交互任務(wù),闡明 canvas 在幫助構(gòu)建 Web 圖形類應(yīng)用時所能夠提供的能力。

發(fā)布日期: 2010 年 12 月 30 日 
級別: 中級 
訪問情況 34 次瀏覽 
建議: 0 (添加評論)

 平均分 (共 0 個評分 )

背景介紹

HTML5 中新引入的 canvas 元素使得 Web 開發(fā)人員在無須借助任何第三方插件(如 Flash,Silverlight)的情況下,可以直接使用 JavaScript 腳本在 Web 頁面進行繪圖。它首次由蘋果公司的 Webkit 框架引入實現(xiàn),并成功運用在 Safari 瀏覽器中,讀者在 這里可以體驗到基于 canvas 的精彩示例。目前,canvas 已成為 HTML5 規(guī)范中的事實性標準,并且已經(jīng)被 Firefox 3.0+, Safari 3.0+, Chrome 3.0+, Opera10.0+ 等瀏覽器所支持。最近(本文撰寫之時),IE 也正式宣稱將在其 9.0 版本之后,開始對 canvas 元素進行支持。

基于 canvas 的繪圖填補了 SVG 繪圖的在復(fù)雜繪圖操作,特別是性能方面的不足,可廣泛應(yīng)用于 Dashboard,2D/3D Game 等 Web 應(yīng)用中。

基本繪圖 API

在了解了什么是 canvas 元素之后,是時候使用 canvas 在 Web 頁面上真正進行的繪圖操作了。實際上,單獨的一個 canvas 標記只是在頁面中定義了一塊矩形區(qū)域,并無特別之處,開發(fā)人員只有配合使用 JavaScript 腳本,才能夠完成各種圖形,線條,以及復(fù)雜的圖形變換操作,與基于 SVG 來實現(xiàn)同樣繪圖效果來比較,canvas 繪圖是一種像素級別的位圖繪圖技術(shù),而 SVG 則是一種矢量繪圖技術(shù)。正鑒于這種本質(zhì)機理的不同,如何更快速高效的進行 canvas 渲染成為各主流 JavaScript 執(zhí)行引擎性能比拼的重要指標之一。目前,Chrome 的 V8, Firefox 的 SpiderMonkey 以及 Safari 的 Nitro 等引擎都已經(jīng)能夠很好的滿足二維繪圖所需的必要性能指標,雖然在運行一些基于 canvas 的游戲時 CPU 占用率還是相對較高,但我們有理由相信隨著 NVIDIA 和 AMD 等一系列硬件廠商的參與,硬件加速技術(shù)將大大提升 Web 應(yīng)用的性能。

在開始繪圖之前,我們需要首先創(chuàng)建一個指定大小的 canvas,并為其指定一個 id,方便在 JavaScript 腳本中獲取該 DOM 實例對象。聲明一個 canvas 節(jié)點的方式如下所示。

 <canvas id="canvas" width="300" height="200"> 
 Fallback content, in case the browser does not support Canvas. 
 </canvas> 

需要指明的是,由于無法保證所有用戶使用的瀏覽器都能夠支持 canvas 元素,所以在目前開發(fā)基于 canvas 的 Web 應(yīng)用中需要增加“Fallback content”,以提示用戶他們無法正常體驗此功能的原因或建議他們?nèi)ハ螺d最新的瀏覽器。

這里,好奇的讀者可能會問,既然這是一個普通的 DOM 節(jié)點,那么便意味著可以通過直接改變其 width 或 height 屬性值來改變 canvas 的大小?確實如此,但是,正如之前提到的 canvas 是一種像素級別的繪圖方法,因而,一旦動態(tài)調(diào)整 canvas 的大小,canvas 將被“重置”到一個新的初始狀態(tài),即便是如下這種操作,也會將 canvas 內(nèi)的位圖清除并將所有相關(guān)屬性恢復(fù)到初始值的狀態(tài)。當然,我們也可以把這當作重置 canvas 的小技巧來使用。

 document.getElementById("canvas").width = document.getElementById("canvas").width; 

簡單圖形繪制

基于 canvas 的繪圖并不是直接在 canvas 標記所創(chuàng)建的繪圖畫面上進行各種繪圖操作,而是依賴畫面所提供的 渲染上下文(Rendering Context),所有的繪圖命令和屬性都定義在渲染上下文當中。在通過 canvas id 獲取相應(yīng)的 DOM 對象之后首先要做的事情就是獲取渲染上下文對象。 渲染上下文與 canvas 一一對應(yīng),無論對同一 canvas 對象調(diào)用幾次 getContext() 方法,都將返回同一個上下文對象。目前,所有支持 canvas 標簽的瀏覽器都支持 2D 渲染上下文,可以使用如下的代碼來獲取該對象。

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

除此之外,在不久的將來,開發(fā)人員還會能夠得到基于 OpenGL 的 3D 渲染上下文以在 canvas 中進行 3D 繪圖。

與 SVG 不同,canvas 原生支持的基本圖形只有矩形一種,至于其他的圓形,多邊形等圖形則都由路徑來負責繪制實現(xiàn)。清單 1 展示了如何使用渲染上下文中的矩形繪圖方法完成了圖 1 所示圖形。


圖 1. 清單 1 對應(yīng)的示例圖形
 

清單 1. 繪制 canvas 矩形
				 
 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 矩形區(qū)域內(nèi)所有像素
 ctx.fillStyle = '#00f';   // 設(shè)置矩形的填充屬性,#00f 代表藍色
 ctx.strokeStyle = '#f00';  // 設(shè)置矩形的線條顏色,#f00 代表紅色
 ctx.fillRect(50,25,150,80); // 使用 fillStyle 填充一個 150*80 大小的矩形
 ctx.strokeRect(45,20, 160, 90);  // 以 strokeStype 屬性為邊的顏色繪制一個無填充矩形
     } 
 } 

繪制路徑

在開始動手繪制路徑之前,首先需要明確的是:矩形繪制 API 是一種即時性的 API,他會在相應(yīng)的繪圖函數(shù)執(zhí)行完畢之后,將圖形即時的渲染在畫面上。然而路徑繪制 API 并非如此,完整的路徑繪制過程大致可以分為如下兩個階段:

  • 定義路徑輪廓:

在每個 canvas 實例對象中都擁有一個 path 對象,創(chuàng)建自定義圖形的過程就是不斷對 path 對象操作的過程。每當開始一次新的圖形繪制任務(wù),都需要先使用 beginPath() 方法來重置 path 對象至初始狀態(tài),進而通過一系列對 moveTo/lineTo 等畫線方法的調(diào)用,繪制期望的路徑,其中 moveTo(x, y) 方法設(shè)置繪圖起始坐標,而 lineTo(x,y) 等畫線方法可以從當前起點繪制直線,圓弧以及曲線到目標位置。最后一步,也是可選的步驟,是調(diào)用 closePath() 方法將自定義圖形進行閉合,該方法將自動創(chuàng)建一條從當前坐標到起始坐標的直線。

  • 繪制路徑

定義完路徑的輪廓,此時 canvas 畫面中沒有顯示任何路徑,開發(fā)人員還可以對路徑進行修改。一旦確定完成,則需要繼續(xù)調(diào)用 stroke()/fill() 函數(shù)來完成將路徑渲染到畫面的最后一步。路徑的輪廓顏色和填充顏色由 strokeStyle 和 fillStyle 屬性決定。

清單 2 繪制一個圖 2 所示半圓弧,并通過 closePath() 方法完成圖形的閉合。


圖 2. 清單 2 對應(yīng)的示例圖形
 

清單 2. 繪制 canvas 路徑
				 
 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();    // 自動繪制一條直線來關(guān)閉弧線。若不調(diào)用此方法,將僅僅顯示一條半圓弧
 ctx.fill();      // 可以嘗試注釋掉 fill 或者 stroke 函數(shù),觀察圖形的變化
 ctx.stroke();  
	 } 
 } 

二維變形

Canvas 繪圖中另一個重要的概念是 繪畫狀態(tài)(Drawing State),繪畫狀態(tài)反映了渲染上下文當前的瞬時狀態(tài),開發(fā)人員可以通過對繪畫狀態(tài)的保存 / 恢復(fù)操作而快速的回到之前使用的各種屬性和變形操作。繪畫狀態(tài)主要由以下三個部分構(gòu)成:

  • 當前的變形矩陣(transformation matrix)
  • 當前的裁剪區(qū)域(clipping region)
  • 當前上下文中的屬性,比如 strokeStyle, fillType, globalAlpha, font 等等。

需要指出的是,當前路徑對象以及當前的位圖都不包含在繪畫狀態(tài)之中,路徑是持續(xù)性的對象,如前文所講,只有通過 beginPath() 操作才會進行重置,而位圖則是 canvas 的屬性,并非屬于渲染上下文的。

開發(fā)人員可以使用 save 和 restore 兩種方法來保存和恢復(fù) canvas 狀態(tài),每調(diào)用 save 方法,都會將當前狀態(tài)壓入堆棧中,而相應(yīng)的 restore 方法則會從堆棧中彈出一個狀態(tài),并將當前畫面恢復(fù)至該狀態(tài)。繪畫狀態(tài)在 canvas 圖形變形操作中應(yīng)用極為廣泛,也非常重要,因為調(diào)用一個 restore 方法遠比手動恢復(fù)先前狀態(tài)要簡單許多,因而,一個較好的習(xí)慣是在做變形操作之前先保存 canvas 狀態(tài)。

二維繪圖的常用變形操作在 canvas 中都可到了很好的支持,包括平移(Translate),旋轉(zhuǎn)(Rotate),伸縮(Scale)等等。由于所有的變形操作都基于變形矩陣,因而開發(fā)人員始終需要記住一點的就是,一旦沒有使用 save/restore 操作保持住原來的繪圖狀態(tài),那么后續(xù)的繪圖操作,都會在當前所應(yīng)用的變形狀態(tài)下完成。清單 3 使用平移和旋轉(zhuǎn)方法繪制了如下所示畫面。


圖 3. 清單 3 所示示例圖形
 

清單 3. 使用平移 / 旋轉(zhuǎn)變形方法繪制復(fù)雜位圖
				 
 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<=2;i++){        // 繪制內(nèi)外 2 層
 if ((i % 2) == 1) {ctx.fillStyle = '#00f';} 
 else{ ctx.fillStyle = '#f00'; } 
 ctx.save();             // 保持開始繪制每一層時的狀態(tài)一致
 for (j=0;j<=i*6;j++){   // 每層生成點的數(shù)量
 ctx.rotate(Math.PI/(3*i));  // 繞當前原點將坐標系順時針旋轉(zhuǎn) Math.Pi/(3*i) 度
				 ctx.beginPath(); 
				 ctx.arc(0,20*i,5,0,Math.PI*2,true); 
 ctx.fill();         // 使用 fillType 值填充每個點
 } 
 ctx.restore();   
		 } 
	 } 
 } 

像素級繪圖

像素級別的繪圖操作是 canvas 繪圖區(qū)別于 SVG,VML 等繪圖技術(shù)的最為明顯特征之一,渲染上下文提供了 createImageData, getImageData, 和 putImageData 三種方法來進行針對像素的操作,所基于的對象都是 imageData 對象。imageData 對象包含 width、height 和 data 三個屬性,其中 data 包含了 width × height × 4 個像素值,之所以乘以 4,在于每個像素都有 RGB 值和透明度 alpha 值。

清單 4 中所示代碼為上一節(jié)中示例圖形增添了簡單的顏色反轉(zhuǎn)濾鏡效果,通過調(diào)用 getImageData(x,y,width,height) 方法獲取以(x,y)為左上坐標的矩形區(qū)域內(nèi)所有像素,而后對所有像素的 RGB 值做取反操作,最后通過 putImageData(imageData, x, y)將修改后的像素值重新繪制到在 canvas 上。


圖 4. 清單 4 所示示例圖形
 

清單 4. 實現(xiàn)簡單濾鏡效果
				 
 function revertImage(){ 
 var canvas = document.getElementById('canvas'); 
 if (canvas.getContext){ 
 var context = canvas.getContext('2d'); 
 // 從指定的矩形區(qū)域獲取 canvas 像素數(shù)組
 var imgdata = context.getImageData(100, 100, 100, 100); 
 var pixels = imgdata.data; 

 // 遍歷每個像素并對 RGB 值進行取反
 for (var i=0, n=pixels.length; i<n; i+= 4){ 
      pixels[i] = 255-pixels[i]; 
       pixels[i+1] = 255-pixels[i+1]; 
       pixels[i+2] = 255-pixels[i+2]; 
 } 
 // 在指定位置進行像素重繪
 context.putImageData(imgdata, 100, 100); 
	 } 
 } 

實現(xiàn)動畫效果

Canvas 并非為了制作動畫而出現(xiàn),自然沒有動畫制作中幀的概念。因而,使用定時器不斷的重繪 canvas 畫面成為了實現(xiàn)動畫效果的通用解決方式。Javascript 中的 setInterval(code,millisec) 方法可以按照指定的時間間隔 millisec 來反復(fù)調(diào)用 code 所指向的函數(shù)或代碼串,這樣,通過將繪圖函數(shù)作為第一個參數(shù)傳給 setInterval 方法,在每次被調(diào)用的過程中移動畫面中圖形的位置,來最終達到一種動畫的體驗。需要注意的一點是,雖然 setinterval 方法的第二個參數(shù)允許開發(fā)人員對繪圖函數(shù)的調(diào)用頻率進行設(shè)定,但這始終都是一種最為理想的情況,由于這種繪圖頻率很大程度上取決于支持 canvas 的底層 JavaScript 引擎的渲染速度以及相應(yīng)繪圖函數(shù)的復(fù)雜性,因而實際運行的結(jié)果往往都是要慢于指定繪圖頻率的。

清單 5 顯示了一個小彈力球動畫效果,在球沒有到達四周邊界時,繪圖方法不斷的移動所繪小球的橫縱坐標。并且,在每次重繪之前,都是用 clear 方法將之前的畫面清除。


清單 5. 實現(xiàn)小彈力球動畫
				 
 <script type="text/javascript"> 
 var x=0,y=0,dx=2,dy=3,context2D;   // 小球從(0,0)開始移動,橫向步長為 2,縱向步長為 3 

 function draw(){ 
 context2D.clearRect(0, 0, canvas.width, canvas.height);   // 清除整個 canvas 畫面
 drawCircle(x, y);         // 使用自定義的畫圓方法,在當前(x,y)坐標出畫一個圓
	
 // 判斷邊界值,調(diào)整 dx/dy 以改變 x/y 坐標變化方向。
 if (x + dx > canvas.width || x + dx < 0) dx = -dx; 
 if (y + dy > canvas.height || y + dy < 0) dy = -dy; 
 x += dx; 
 y += dy; 
 } 

 window.onload = function (){ 
 var canvas = document.getElementById('canvas'); 
 context2D = canvas.getContext('2d'); 
 setInterval(draw, 20);     // 設(shè)置繪圖周期為 20 毫秒
 } 
 </script> 

提高可訪問性

一款優(yōu)秀的 Web 應(yīng)用必須要做到的就是提供給用戶很好的可訪問性,這包括對鼠標,鍵盤以及快捷鍵等操作的響應(yīng),canvas 畫面的本質(zhì)仍是一個 DOM 節(jié)點,因而開發(fā)人員可以通過常規(guī)的方法來處理響應(yīng)。這里,與基于 SVG 的繪圖不同,由于 SVG 是一種基于 XML 的聲明式的繪圖方式,因而,SVG 中任何的圖形都可以作為一個獨立的 DOM 節(jié)點去接收并響應(yīng)特定事件,而 canvas 由于其像素繪圖的本質(zhì),則只可以在 canvas 元素節(jié)點去處理。

圖 5 所示示例代碼,當鼠標在 canvas 中移動時,鼠標當前相對于 canvas 中的橫縱坐標將實時輸出到上方提示信息區(qū)域;當用戶在 canvas 中單擊鼠標左鍵,將在相應(yīng)位置創(chuàng)建一個藍色小球,而后用戶可以通過鍵盤上的左 / 右方向鍵對藍色小球進行控制,使其進行橫向的移動。示例代碼如清單 6 所示。


圖 5. 清單 6 所示示例展現(xiàn)
 

清單 6. 實現(xiàn) canvas 對方向鍵和鼠標點擊事件的響應(yīng)
				 
 <script type="text/javascript"> 
 var g_x,g_y;    // 鼠標當前的坐標
 var g_pointx, g_pointy;   // 藍色小球當前的坐標
	 var canvas; 
	
 function drawCircle(x,y){    // 以鼠標當前位置為原點繪制一個藍色小球
 var ctx = canvas.getContext('2d'); 
		 ctx.clearRect(0,0,300,300); 
		 ctx.fillStyle = '#00f'; 
		 ctx.beginPath(); 
		 ctx.arc(x,y,20,0,Math.PI*2,true); 
		 ctx.fill(); 
			
 g_pointx = x; 
		 g_pointy = y 
 } 
	
	 function onMouseMove(evt) { 
 // 獲取鼠標在 canvas 中的坐標位置
 if (evt.layerX || evt.layerX == 0) { // FireFox 
 g_x = evt.layerX; 
 g_y = evt.layerY; 
 } 
		 document.getElementById("xinfo").innerHTML = g_x; 
		 document.getElementById("yinfo").innerHTML = g_y; 
	 } 

	 function onKeyPress(evt) { 
 var dx = 3;  // 橫向平移步長
		 var kbinfo = document.getElementById("kbinfo"); 
		
 if (evt.keyCode == 39){   
			 kbinfo.innerHTML="right"; 
 if (g_x<300-dx) drawCircle(g_pointx+dx,g_pointy); 
 document.getElementById("xinfo").innerHTML = g_pointx; 
		 }else if (evt.keyCode == 37){ 
 kbinfo.innerHTML = "left"; 
			 if (g_x>dx) drawCircle(g_pointx-dx,g_pointy); 
 document.getElementById("xinfo").innerHTML = g_pointx; 
 } 
 } 
			
 window.onload = function(){ 
 canvas = document.getElementById('canvas'); 
 // 增加 canvas 節(jié)點對鼠標單擊,移動以及鍵盤事件的響應(yīng)函數(shù)
		 canvas.addEventListener('click', function(evt){drawCircle(g_x, g_y);} , false);
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error:  The previous line is longer than the max of 90 characters ---------|
		 canvas.addEventListener('mousemove', onMouseMove, false); 
 canvas.addEventListener('keypress', onKeyPress, false); 
 canvas.focus();  // 獲得焦點之后,才能夠?qū)︽I盤事件進行捕獲
 } 
 </script> 

這里我們對鼠標的移動,單擊操作進行響應(yīng),在實際應(yīng)用中可以視特定應(yīng)用的需求,增加對鼠標摁下,松開或雙擊等更為豐富操作的響應(yīng),增強應(yīng)用的可訪問性。

細心的讀者可能發(fā)現(xiàn),在通過不斷重繪畫面以達到動畫效果的過程中,我們的重繪方法首先做的事情都是調(diào)用 clearRect(x, y, width, height) 方法將原畫面清空,這種銷毀而后重繪的方式丟失了之前的畫面,使得開發(fā)人員不得不重繪整幅畫面,這在性能上是難以接受的,一種可行的做法是通過多個 canvas 疊加的方式,根據(jù)不同 canvas 上的不同刷新頻率,分別完成各自的重繪任務(wù)。這種多 canvas 技巧,在處理繪圖類應(yīng)用中最為常見的“撤銷”操作時也非常有效,所有的繪圖都發(fā)生在上層 canvas,只有被用戶確認的畫面,才會被繪制到底層 canvas 上。鑒于本文所討論技術(shù)范圍,這里不做過多講解,有興趣的讀者可以通過本文參考文獻所列資源,進行進一步的深入學(xué)習(xí)。

總結(jié)

本文對 HTML5 新引入的 canvas 元素在 Web 繪圖中所扮演的角色和所發(fā)揮的作用做了最基本的介紹,其中包括使用 canvas 完成基本的 Web 繪圖,動畫和交互任務(wù),雖然 Flash,Silverlight 也都可以完成相同的任務(wù),甚至在性能上更勝一籌,但是作為一種不依賴任何插件的標準 Web 像素級繪圖技術(shù),我們有理由相信隨著各大瀏覽器廠商的加入,canvas 將會更加成熟完善,也會有更多基于 canvas 的繪圖類應(yīng)用不斷涌現(xiàn)。

聲明

本人所發(fā)表的內(nèi)容僅為個人觀點,不代表 IBM 公司立場、戰(zhàn)略和觀點。


參考資料

學(xué)習(xí)

討論

關(guān)于作者

林林,畢業(yè)于北京郵電大學(xué),獲得計算機碩士學(xué)位?,F(xiàn)就職于 IBM 中國軟件開發(fā)中心 ETI 部門,從事 Web 2.0 相關(guān)產(chǎn)品的研發(fā)工作,對 Java EE,語義 Web,敏捷開發(fā)以及三網(wǎng)融合相關(guān)技術(shù)有著濃厚興趣和豐富經(jīng)驗。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Web 開發(fā)中的矢量繪圖處理和應(yīng)用
微信小程序中的canvas
清除Canvas
canvas
網(wǎng)頁|HTML5 也可以畫一畫(canvas)
JavaScript圖形實例:Canvas API
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服