第十五章 使用Canvas繪圖javascript
HTML5添加的<canvas>元素負責在頁面中設定一個區域,而後就能夠經過JavaScript動態地在這個區域中繪製圖形。 如:<canvas id="drawing" width=" 200" height="200">A drawing of something.</canvas> html
在使用<canvas>元素以前,首先要檢測getContext()方法是否存在,這一步很是重要。有些瀏覽器會爲HTML規範以外的元素建立默認的HTML元素對象。在這種狀況下,即便 drawing 變量中保存着一個有效的元素引用,也檢測不到 getContext()方法。java
1、使用2D上下文web
使用2D繪圖上下文提供的方法,能夠繪製簡單的2D圖形,好比矩形、弧線和路徑。2D上下文的座標開始於<canvas>元素的左上角,原點座標是(0,0)。全部座標值都基於這個原點計算,x值越大表示越靠右,y值越大表示越靠下。默認狀況下,width和height表示水平和垂直兩個方向上可用的像素數目。 編程
一、填充和描邊 canvas
填充和描邊兩個操做取決於兩個屬性:fillStyle 和 strokeStyle。這兩個屬性的值能夠是字符串、漸變對象或模式對象,並且它們的默認值都是"#000000"。數組
二、繪製矩形瀏覽器
與矩形有關的方法包括 fillRect()、 strokeRect()和 clearRect()。服務器
fillRect()方法在畫布上繪製的矩形會填充指定的顏色,填充的顏色經過fillStyle屬性指定;strokeRect()方法在畫布上繪製的矩形會使用指定的顏色描邊,描邊顏色經過strokeStyle屬性指定,clearRect()方法用於清除畫布上的矩形區域。app
下面是一個例子:
1 <html> 2 <head> 3 <script type="text/javascript" src="myscript.js"></script> 4 </head> 5 <body> 6 <canvas id="drawing" width=" 200" height="200">A drawing of something.</canvas> 7 </body> 8 </html>
1 window.onload = function(){ 2 var drawing = document.getElementById("drawing"); 3 //肯定瀏覽器支持<canvas>元素 4 if(drawing.getContext){ 5 var context = drawing.getContext("2d"); 6 //繪製紅色矩形 7 context.fillStyle = "#ff0000"; 8 context.fillRect(10, 10, 50, 50); 9 //繪製半透明的藍色矩形 10 context.fillStyle = "rgba(0,0,255,0.5)"; 11 context.fillRect(30, 30, 50, 50); 12 //繪製黑色描邊矩形 13 context.strokeStyle = "#000000"; 14 context.strokeRect(10, 10, 50, 50); 15 //繪製半透明的綠色描邊矩形 16 context.strokeStyle = "rgba(0,255,0,0.5)"; 17 context.strokeRect(30, 30, 50, 50); 18 //在兩個矩形重疊的地方清除一個小矩形 19 context.clearRect(40, 40, 10, 10); 20 } 21 }
效果圖:
三、繪製路徑
要繪製路徑,首先必須調用beginPath()方法,而後可調用其餘方法繪製路徑,經常使用方法(省略了參數):arc()、arcTo()、bezierCurveTo()、lineTo()、moveTo()、quadraticCurveTo()、rect()。
如繪製一個不帶數字的時鐘錶盤:
1 window.onload = function(){ 2 var drawing = document.getElementById("drawing"); 3 //肯定瀏覽器支持<canvas>元素 4 if(drawing.getContext){ 5 var context = drawing.getContext("2d"); 6 //開始路徑 7 context.beginPath(); 8 //繪製外圓 9 context.arc(100, 100, 99, 0, 2 * Math.PI, false); 10 //繪製內圓 11 //在繪製內圓以前,須把路徑移動到內圓上的某一點,避免繪製出多餘的線條。 12 context.moveTo(194, 100); 13 context.arc(100, 100, 94, 0, 2 * Math.PI, false); 14 //繪製分針 15 context.moveTo(100, 100); 16 context.lineTo(100, 15); 17 //繪製時針 18 context.moveTo(100, 100); 19 context.lineTo(35, 100); 20 //描邊路徑 21 context.stroke(); 22 } 23 }
效果圖:
四、繪製文本
繪製文本主要有兩個方法:fillText()和strokeText()。這兩個方法均可以接收4個參數:要繪製的文本字符串、x座標、y座標和可選的大像素寬度。
因爲繪製文本比較複雜,特別是須要把文本控制在某一區域中的時候,2D上下文提供了輔助肯定文本大小的方法measureText()。這個方法接收一個參數,即要繪製的文本,返回一個TextMetrics 對象。返回的對象目前只有一個 width 屬性,但未來還會增長更多度量屬性。
measureText()方法利用 font、textAlign和textBaseline的當前值計算指定文本的大小,假如想在一個140像素寬的矩形區域中繪製文本Hello world!,下面的代碼從100像素的字體大小開始遞減,最終找到合適的字體大小。
1 var fontSize = 100; context.font = fontSize + "px Arial"; 2 while(context.measureText("Hello world!").width > 140){ 3 fontSize--; 4 context.font = fontSize + "px Arial"; 5 } 6 context.fillText("Hello world!", 10, 10); 7 context.fillText("Font size is " + fontSize + "px", 10, 50);
五、變換
經過上下文的變換,能夠把處理後的圖像繪製到畫布上。經常使用方法:
rotate(angle):圍繞原點旋轉圖像 angle 弧度;
scale(scaleX, scaleY):縮放圖像;
translate(x,y):將座標原點移動到(x,y);
transform(m1_1, m1_2, m2_1, m2_2, dx, dy):直接修改變換矩陣,
setTransform(m1_1, m1_2, m2_1, m2_2, dx, dy):將變換矩陣重置爲默認狀態,而後再調用transform()。
例如:能夠把原點變換到時鐘錶盤的中心點(100,100),全部數學計算都基於(0,0),而不是(100,100)。還可使用 rotate()方法旋轉時鐘的錶針。
代碼以下:
1 window.onload = function(){ 2 var drawing = document.getElementById("drawing"); 3 //肯定瀏覽器支持<canvas>元素 4 if(drawing.getContext){ 5 var context = drawing.getContext("2d"); 6 //開始路徑 7 context.beginPath(); 8 //繪製外圓 9 context.arc(100, 100, 99, 0, 2 * Math.PI, false); 10 //繪製內圓 11 context.moveTo(194, 100); 12 context.arc(100, 100, 94, 0, 2 * Math.PI, false); 13 //變換原點 14 context.translate(100, 100); 15 //旋轉錶針 16 context.rotate(1); 17 //繪製分針 18 context.moveTo(0,0); 19 context.lineTo(0, -85); 20 //繪製時針 21 context.moveTo(0, 0); 22 context.lineTo(-65, 0); 23 //描邊路徑 24 context.stroke(); 25 } 26 }
效果圖:
不管是剛纔執行的變換,仍是fillStyle、strokeStyle 等屬性,都會在當前上下文中一直有效, 除非再對上下文進行什麼修改,經過save()、 restore()方法可保存和恢復上下文,這兩個方法經過一個棧來實現。
六、繪製圖像
若是想把一幅圖像繪製到畫布上,可使用drawImage()方法,結合其餘方法,能夠對圖像進行各類基本操做,操做的結果能夠經過 toDataURL()方法得到,有一個例外,即圖像不能來自其餘域。若是圖像來自其餘域,調用 toDataURL()會拋出一個錯誤。
七、陰影
2D上下文會根據如下幾個屬性的值,自動爲形狀或路徑繪製出陰影。
shadowColor:用CSS顏色格式表示的陰影顏色,默認爲黑色;
shadowOffsetX:形狀或路徑x軸方向的陰影偏移量,默認爲0。
shadowOffsetY:形狀或路徑y軸方向的陰影偏移量,默認爲0。
shadowBlur:模糊的像素數,默認0,即不模糊。
八、漸變
漸變由 CanvasGradient 實例表示,很容易經過 2D上下文來建立和修改。要建立一個新的線性漸變,能夠調用createLinearGradient()方法; addColorStop()方法用來指定漸變的色標。
以下是一個例子:
1 window.onload = function(){ 2 //這個函數基於起點的x和y座標以及寬度和高度來建立漸變對象,使得在fillRect()中可用相同的值,便於控制。 3 function createRectLinearGradient(context, x, y, width, height){ 4 return context.createLinearGradient(x, y, x+width, y+height); 5 } 6 var drawing = document.getElementById("drawing"); 7 if(drawing.getContext){ 8 var context = drawing.getContext("2d"); 9 //繪製紅色矩形 10 context.fillStyle = "#ff0000"; 11 context.fillRect(10, 10, 50, 50); 12 var gradient = createRectLinearGradient(context, 30, 30, 50, 50); 13 gradient.addColorStop(0, "white"); 14 gradient.addColorStop(1, "black"); 15 //繪製漸變矩形 16 context.fillStyle = gradient; 17 context.fillRect(30, 30, 50, 50); 18 } 19 }
效果圖:
若是要建立徑向漸變(或放射漸變),可使用 createRadialGradient()方法。
代碼:
window.onload = function(){ var drawing = document.getElementById("drawing"); if(drawing.getContext){ var context = drawing.getContext("2d"); 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); } }
效果圖:
九、模式
模式其實就是重複的圖像,能夠用來填充或描邊圖形。要建立一個新模式,能夠調用 createPattern()方法並傳入兩個參數:一個 HTML<img>元素和一個表示如何重複圖像的字符串。
十、使用圖像數據
2D上下文的一個明顯的長處就是,能夠經過 getImageData()取得原始圖像數據。這個方法接收 4個參數:要取得其數據的畫面區域的x和y座標以及該區域的像素寬度和高度。
例如,要取得左上角座標爲(10,5)、大小爲 50×50像素的區域的圖像數據,可使用如下代碼:
var imageData = context.getImageData(10, 5, 50, 50);
這裏返回的對象是ImageData的實例。每一個ImageData對象都有三個屬性:width、height和data。其中data屬性是一個數組,保存着圖像中每個像素的數據。在data數組中,每個像素用4個元素來保存,分別表示紅、綠、藍和透明度值。所以,第一個像素的數據就保存在數組的第0到第3個元素中能夠據此直接操做原始圖像數據。
十一、合成
還有兩個會應用到 2D上下文中全部繪製操做的屬性:globalAlpha 和 globalComposition-Operation。
其中,globalAlpha 是一個介於 0和 1之間的值(包括0和1),用於指定全部繪製的透明度。默認值爲0。若是全部後續操做都要基於相同的透明度,就能夠先把 globalAlpha設置爲適當值,而後繪製,後再把它設置回默認值0。
globalCompositionOperation屬性則表示後繪製的圖形怎樣與先繪製的圖形結合。這個屬性的值是字符串。
2、WebGL
WebGL上下文是一種3D上下文。WebGL是從OpenGL ES 2.0移植到瀏覽器中的,OpenGL ES 2.0是遊戲開發人員在建立計算機圖形圖像時常用的一種語言。
WebGL支持比 2D上下文更豐富 和更強大的圖形圖像處理能力,好比:
a、用GLSL編寫的頂點和片斷着色器;
b、支持類型化數組,即可以將數組中的數據限定爲某種特定的數值類型;
c、建立和操做紋理;
從穩妥的角度考慮,在使用 WebGL以前,最好檢測其是否獲得了支持。WebGL 仍是一個正在制定和發展中的規範。無論是函數名、函數簽名,仍是數據類型,都有可能改變。能夠說,WebGL目前只適合實驗性地學習,不適合真正開發和應用。
第十六章、HTML5腳本編程
HTML5除了定義了新的標記規則,還定義了一些 JavaScript API。這些 API是爲了讓開發人員建立出更好的、可以與桌面應用媲美的用戶界面而設計的。
1、跨文檔消息傳遞
跨文檔消息傳送,有時候簡稱爲XDM,指的是在來自不一樣域的頁面間傳遞消息。
XDM的核心是postMessage()方法。在HTML5規範中,除了XDM部分以外的其餘部分也會提到這個方法名,但都是爲了同一個目的:向另外一個地方傳遞數據。對於XDM而言,「另外一個地方」指的是包含在當前頁面中的<iframe>元素,或者由當前頁面彈出的窗口。
接收到 XDM消息時,會觸發 window 對象的message事件。這個事件是以異步形式觸發的。
2、原生拖放
原生拖放功能讓咱們能夠方便地指定某個元素可拖動,並在操做系統要放置時作出響應。還能夠建立自定義的可拖動元素及放置目標。
一、 與拖放相關的事件有:dragstart、drag、dragend、dragenter、dragover、dragleave(或drop);
二、 dataTransfer 對象,它是事件對象的一個屬性,用於從被拖動元素向放置目標傳遞字符串格式的數據。dataTransfer對象有兩個主要方法:getData()和 setData()。
如:
//設置和接收文本數據
event.dataTransfer.setData("text", "some text");
var text = event.dataTransfer.getData("text");
//設置和接收 URL
event.dataTransfer.setData("URL", "http://www.wrox.com/");
var url = event.dataTransfer.getData("URL");
三、利用 dataTransfer 對象,還能經過它來肯定被拖動的元素以及做爲放置目標的元素可以接收什麼操做。爲此,須要訪問dataTransfer對象的兩個屬性:dropEffect和 effectAllowed。
四、HTML5還爲全部 HTML元素規定了一個 draggable屬性,表示元素是否能夠拖動。
3、媒體元素
HTML5新增了<audio>和<video>這兩個與媒體相關的標籤,讓開發人員沒必要依賴任何插件就能在網頁中嵌入跨瀏覽器的音頻和視頻內容。
用法以下:
<!-- 嵌入視頻 --> <video src="conference.mpg" id="myVideo">Video player not available.</video> <!-- 嵌入音頻 --> <audio src="song.mp3" id="myAudio">Audio player not available.</audio>
使用這兩個元素時,至少要在標籤中包含src屬性,指向要加載的媒體文件。還能夠設置width和height屬性以指定視頻播放器的大小,而爲poster屬性指定圖像的URI能夠在加載視頻內容期間顯示一幅圖像。另外,若是標籤中有controls屬性,則意味着瀏覽器應該顯示UI控件,以便用戶直接操做媒體。
由於並不是全部瀏覽器都支持全部媒體格式,因此能夠指定多個不一樣的媒體來源。爲此,不用在標籤中指定src 屬性,而是要像下面這樣使用一或多個<source>元素。
<!-- 嵌入視頻 --> <video id="myVideo"> <source src="conference.webm" type="video/webm; codecs='vp8, vorbis'"> <source src="conference.ogv" type="video/ogg; codecs='theora, vorbis'"> <source src="conference.mpg"> Video player not available. </video> <!-- 嵌入音頻 --> <audio id="myAudio"> <source src="song.ogg" type="audio/ogg"> <source src="song.mp3" type="audio/mpeg"> Audio player not available. </audio>
這兩個媒體元素有許多屬性,還能夠觸發不少的事件。使用<audio>和<video>元素的 play()和 pause()方法,能夠手工控制媒體文件的播放。組合使用屬性、事件和這兩個方法,很容易建立一個自定義的媒體播放器,以下面的例子所示。
<div class="mediaplayer"> <div class="video"> <video id="player" src="movie.mov" poster="mymovie.jpg" width="300" height="200"> Video player not available. </video> </div> <div class="controls"> <input type="button" value="Play" id="video-btn"> <span id="curtime">0</span>/<span id="duration">0</span> </div> </div>
//取得元素的引用 var player = document.getElementById("player"), btn = document.getElementById("video-btn"), curtime = document.getElementById("curtime"), duration = document.getElementById("duration"); //更新播放時間 duration.innerHTML = player.duration; //爲按鈕添加事件處理程序 EventUtil.addHandler(btn, "click", function(event){ if (player.paused){ player.play(); btn.value = "Pause"; } else { player.pause(); btn.value = "Play"; } }); //定時更新當前時間 setInterval(function(){ curtime.innerHTML = player.currentTime; }, 250);
以上JavaScript代碼給按鈕添加了一個事件處理程序,單擊它能讓視頻在暫停時播放,在播放時暫停。經過<video>元素的load事件處理程序,設置了加載完視頻後顯示播放時間。最後,設置了一個 計時器,以更新當前顯示的時間。
4、歷史狀態管理
歷史狀態管理讓咱們沒必要卸載當前頁面便可修改瀏覽器的歷史狀態棧。有了這種機制,用戶就能夠經過「後退」和「前進」按鈕在頁面狀態間切換,而這些狀態徹底由JavaScript進行控制。要管理歷史狀態首選使用hashchange事件。HTML5經過更新history對象爲管理歷史狀態提供了方便。
第十七章 錯誤處理與調試
1、錯誤處理
一、try-catch語句
常見用法:
1 try {// 可能會致使錯誤的代碼 2 window.someNonexistentFunction(); 3 } catch (error){ // 在錯誤發生時怎麼處理 4 alert(error.message); 5 }
二、finally 子句
finally在try-catch語句中是可選的,但finally子句一經使用,其代碼不管如何都會執行。注意,只要代碼中包含finally子句,那麼不管 try仍是catch語句塊中的return語句都將被忽略。所以,在使用finally子句以前,必定要很是清楚你想讓代碼怎麼樣。
1 function testFinally(){ 2 try { 3 return 2; 4 } catch (error){ 5 return 1; 6 } finally { 7 return 0; 8 } 9 }
上述代碼,執行後函數會返回0。
三、錯誤類型
ECMA-262 定義了下列7種錯誤類型:Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError。
Error是基類型,其餘錯誤類型都繼承自該類型。
四、拋出錯誤
與try-catch語句相配的還有一個throw操做符,用於隨時拋出自定義錯誤。拋出錯誤時,必須 要給throw操做符指定一個值,這個值是什麼類型,沒有要求。
在遇到throw操做符時,代碼會當即中止執行。僅當有try-catch語句捕獲到被拋出的值時,代碼纔會繼續執行。
拋出錯誤示範:throw new Error("Something bad happened.");
還能夠建立拋出自定義的錯誤,下面是利用原型鏈繼承Error來建立自定義錯誤類型:
1 //建立自定義錯誤類型 2 function CustomError(message){ 3 this.name = "CustomError"; 4 this.message = message; 5 } 6 CustomError.prototype = new Error(); 7 throw new CustomError("My message");
拋出錯誤的時機:應該在出現某種特定的已知錯誤條件,致使函數沒法正常執行時拋出錯誤,如:
1 function process(values){ 2 if (!(values instanceof Array)){ 3 throw new Error("process(): Argument must be an array."); 4 } 5 values.sort(); 6 for (var i=0, len=values.length; i < len; i++){ 7 if (values[i] > 100){ 8 return values[i]; 9 } 10 } 11 return -1; 12 }
五、錯誤(error)事件
任何沒有經過try-catch處理的錯誤都會觸發window對象的error事件。要指定onerror事件處理程序,必須使用以下所示的DOM0級技術,它沒有遵循「DOM2級事件」的標準格式。
window.onerror = function(message, url, line){ alert(message); };
圖像也支持 error 事件。只要圖像的src特性中的URL不能返回能夠被識別的圖像格式,就會觸發error事件。此時的error事件遵循 DOM格式,會返回一個以圖像爲目標的event對象,下面是 一個例子。
1 var image = new Image(); 2 EventUtil.addHandler(image, "load", function(event){ 3 alert("Image loaded!"); 4 }); 5 EventUtil.addHandler(image, "error", function(event){ 6 alert("Image not loaded!"); 7 }); 8 image.src = "smilex.gif"; //指定不存在的文件
在這個例子中,當加載圖像失敗時就會顯示一個警告框。須要注意的是,發生error事件時,圖像下載過程已經結束,也就是說不能再從新下載了。
六、常見的致使錯誤的類型:
a、類型轉換錯誤:類型轉換錯誤發生在使用某個操做符,或者使用其餘可能會自動轉換值的數據類型的語言結構時。在使用相等 (==)和不相等(!=)操做符,或者在 if、 for 及 while 等流控制語句中使用非布爾值時,最常發生類型轉換錯誤。
b、數據類型錯誤:在將預料以外的值傳遞給函數的狀況下,最容易發生數據類型錯誤。
c、通訊錯誤:第一種通訊錯誤與格式不正確的 URL 或發送的數據有關。最多見的問題是在將數據發送給服務器以前,沒有使用 encodeURIComponent()對數據進行編碼。 另外,在服務器響應的數據不正確時,也會發生通訊錯誤。
七、非致命錯誤:不影響用戶的主要任務;隻影響頁面的一部分;能夠恢復;重複相同操做能夠消除錯誤。
致命錯誤: 應用程序根本沒法繼續運行; 錯誤明顯影響到了用戶的主要操做; 會致使其餘連帶錯誤。
八、開發 Web 應用程序過程當中的一種常見的作法,就是集中保存錯誤日誌,以便查找重要錯誤的緣由。推薦把JavaScript錯誤回寫到服務器。
2、調試技術
一、將消息記錄到控制檯
對 IE八、 Firefox、 Chrome 和 Safari 來講,能夠經過 console 對象向 JavaScript 控制檯中寫入消息,這個對象具備下列方法。
a、error(message):將錯誤消息記錄到控制檯
b、info(message):將信息性消息記錄到控制檯
c、log(message):將通常消息記錄到控制檯
d、warn(message):將警告消息記錄到控制檯
如:
function sum(num1, num2){ console.log("Entering sum(), arguments are " + num1 + "," + num2); console.log("Before calculation"); var result = num1 + num2; console.log("After calculation"); console.log("Exiting sum()"); return result; }
還有一種方案是使用 LiveConnect,也就是在 JavaScript 中運行Java代碼。 Firefox、 Safari 和 Opera都支持LiveConnect,所以能夠操做Java 控制檯。
1 function sum(num1, num2){ 2 java.lang.System.out.println("Entering sum(), arguments are " + num1 + "," + num2); 3 java.lang.System.out.println("Before calculation"); 4 var result = num1 + num2; 5 java.lang.System.out.println("After calculation"); 6 java.lang.System.out.println("Exiting sum()"); 7 return result; 8 }
二、將消息記錄到當前頁面
另外一種輸出調試消息的方式,就是在頁面中開闢一小塊區域,用以顯示消息。這個區域一般是一個元素,而該元素能夠老是出如今頁面中,但僅用於調試目的;也能夠是一個根據須要動態建立的元素。 例如,能夠將 log()函數修改成以下所示:
1 function log(message){ 2 var console = document.getElementById("debuginfo"); 3 if (console === null){ 4 console = document.createElement("div"); 5 console.id = "debuginfo"; 6 console.style.background = "#dedede"; 7 console.style.border = "1px solid silver"; 8 console.style.padding = "5px"; 9 console.style.width = "400px"; 10 console.style.position = "absolute"; 11 console.style.right = "0px"; 12 console.style.top = "0px"; 13 document.body.appendChild(console); 14 } 15 console.innerHTML += "<p>" + message + "</p>"; 16 }
3、常見的 IE錯誤
常見IE錯誤有:操做終止、無效字符、未找到成員、未知運行時錯誤、語法錯誤、系統沒法找到指定資源等