21.1節介紹如何用傳統的JavaScript技術實現諸如圖片翻轉(鼠標指針移動到一張靜態圖片上切換成另一張圖片)這樣的視覺效果。html
21.2節介紹HTML5的<audio>和<video>元素以及它們的JavaScript API。程序員
21.3和21.4介紹兩項很是強大的用於客戶端繪圖的技術。可以在瀏覽器中動態生成複雜圖形是很是重要的,由於:web
21.3節介紹可伸縮的矢量圖形(Scalable Vector Graphics,SVG)。SVG是一種基於XML的而且用於描述圖形的語言,SVG圖形能夠經過JavaScript和DOM來建立和操控。canvas
21.4節會介紹HTML5的<canvas>元素及其用於客戶端畫圖的、功能齊全的JavaScript API。<canvas>元素是一項革命性的技術。數組
以下的HTML代碼段是一個很是簡單的例子:它建立一張圖片,並在鼠標指針通過的時候改變該圖片:瀏覽器
<img src="images/help.gif"
onmouseover="this.src='images/help_rollover.gif'"
onmouseout="this.src='images/help.gif'">
爲了有用起見,像圖片翻轉這樣的效果須要較高響應度。這也意味着須要想辦法來確保一些必要的圖片要預提取,讓瀏覽器緩存起來。客戶端JavaScript定義了一個專用的API來達到這一目的:爲了強制讓圖片緩存起來,首先利用Image()構造函數來建立一個屏幕外圖片對象,以後,將該對象的src屬性設置成指望的URL。因爲圖片元素並無添加到文檔中,所以,它是不可見的,可是瀏覽器仍是會加載圖片並將其緩存起來。這樣一來,以後當設置成一樣的URL來顯示該屏幕內圖片的時候,它就能很快從瀏覽器緩存中加載,而不須要再經過網絡加載。緩存
前面展現的圖片翻轉的代碼片斷並無預提取它使用的翻轉圖片,這樣,當用戶第一次將鼠標指針移到圖片上的時候會明顯感到翻轉效果有延時。要解決這個問題,將代碼修改爲以下形式:服務器
<script>(new Image()).src="images/help_rollover.gif";</script> <img src="images/help.gif" onmouseover="this.src='images/help_rollover.gif'" onmouseout="this.src='images/help.gif'">
優雅的圖片翻轉實現方式網絡
上述代碼用了一個<script>元素和兩個JavaScript事件處理程序的屬性來實現一個簡單的圖片翻轉效果。這個例子的代碼很是不優雅:大量的JavaScript和HTML代碼混在一塊兒。架構
例子中展現了一種更爲優雅的實現方式,這種方式容許在任意的<img>元素上,只要簡單地指定了data-rollover屬性(參見15.4.3節),就會建立一個圖片翻轉效果。要注意的是,該例使用了例13-5中介紹的onLoad()函數。同時它還用到了document.images[]數組(參見15.2.3節)從文檔中查找全部的<img>元素。
例21-1:優雅的圖片翻轉實現方式。
<audio src="background_music.mp3"/> <video src="news.mov"width=320 height=240/>
因爲各家瀏覽器製造商未能在對標準音頻和視頻編解碼器支持上達成一致,所以,一般都須要使用<source>元素來爲指定不一樣格式的媒體源:
<audio id="music"> <source src="music.mp3"type="audio/mpeg"> <source src="music.ogg"type='audio/ogg;codec="vorbis"'> </audio>
支持<audio>和<video>元素的瀏覽器不會渲染這些元素的內容。而不支持它們的瀏覽器則會將它們的內容都渲染出來,所以,能夠在這些元素中放置後備內容(好比,一個用於調用Flash插件的<object>元素)
<video id="news"width=640 height=480 controls preload> <!--Firefox和Chrome支持的WebM格式--> <source src="news.webm"type='video/webm;codecs="vp8,vorbis"'> <!--IE和Safari支持的H.264格式--> <source src="news.mp4"type='video/mp4;codecs="avc1.42E01E,mp4a.40.2"'> <!--Flash插件做爲後備方案--> <object width=640 height=480 type="application/x-shockwave-flash" data="flash_movie_player.swf"> <!--這裏的參數元素用於配置Flash視頻播放器--> <!--文本是最終的後備內容--> <div>video element not supported and Flash plugin not installed.</div> </object> </video>
Audio()構造函數
在不設置controls屬性的狀況下,<audio>元素沒有任何視覺外觀。正如可使用Image()構造函數來建立一張屏幕外圖片那樣,HTML5中的媒體API一樣也容許使用Audio()構造函數,並將媒體源URL做爲參數,來建立一個屏幕外音頻元素:new Audio("chime.wav").play();//載入並播放聲音效果。Audio()構造函數的返回值和經過從文檔中查詢<audio>元素或者使用document.createElement("audio")來建立一個新的元素得到的都是同一類對象。這裏要注意的是,Audio()是音頻元素特有的API,換句話說,視頻元素是沒有相似Video()這樣的構造函數的。
儘管對於多種不一樣格式的文件要分別定義媒體比較繁瑣,可是,可以不借助插件在瀏覽器中原生播放音頻和視頻是HTML5中很是強大的新特性。要注意的是,對於媒體編解碼器的問題以及瀏覽器對其兼容性的問題並不在本書討論的範疇。接下來會集中討論如何利用JavaScript API來操控音頻和視頻流。
想要測試一個媒體元素可否播放指定類型的媒體文件,能夠調用canPlayType()方法並將媒體的MIME類型(有時須要包含codec參數)傳遞進去。若是它不能播放該類型的媒體文件,該方法會返回一個空的字符串(一個假值);反之,它會返回一個字符串:"maybe"或者"probably"。之因此返回"probably"這樣不肯定的結果,是由於音頻和視頻編解碼器自己就很是複雜,在沒有真正下載並嘗試播放指定類型的媒體前很難肯定是否真的能夠支持播放此類型文件:
var a=new Audio();
if(a.canPlayType("audio/wav")){
a.src="soundeffect.wav";
a.play();
}
當設置媒體元素的src屬性的時候,加載媒體的過程就開始了(除非將preload設置成"auto",不然,只會加載少許內容,所以該過程不會持續很長時間)。當設置src屬性的時候,若是有其餘的媒體文件正在加載或者播放,則會停止它們的加載或者播放過程。若是經過在媒體元素中添加<source>元素而不是設置src屬性的方式指定媒體源,媒體元素沒法知道是否已經將一系列<source>元素都添加完畢了,所以它也不會開始選擇並加載<source>元素指定的媒體源文件,除非顯式地調用load()方法。
//文檔載入完成後,開始播放背景音樂 window.addEventListener("load",function(){document.getElementById("music").play(); },false);
例如,假設媒體文件從開始緩存起中間沒有定點播放發生(跳過一段播放),可使用以下代碼來肯定當前緩存內容的百分比:
var percent_loaded=Math.floor(song.buffered.end(0)/song.duration*100);
readyState屬性指定當前已經加載了多少媒體內容,所以同時也暗示着是否已經準備好能夠播放了。以下表格展現了該屬性的取值以及對應的意義:
NetworkState屬性指定媒體元素是否使用網絡或者爲何媒體文件不使用網絡:
當在加載媒體或者播放媒體過程當中發生錯誤時,瀏覽器就會設置<audio>或者<video>元素的error屬性。在沒有錯誤發生的狀況下,error屬性值爲null。反之,error的屬性值是一個對象,包含了描述錯誤的數值code屬性。同時,error對象也定義了一些描述可能的錯誤代碼的常量:
能夠以以下方式使用error屬性:
if(song.error.code==song.error.MEDIA_ERR_DECODE) alert("Can't play song:corrupt audio data.");
下表根據它們觸發的前後順序,總結了22個媒體相關事件。這些事件不能經過屬性來註冊事件,只能經過<audio>和<video>元素的addEventListener()方法來註冊處理程序函數。
一個簡單的SVG文件以下所示:
<!--SVG圖形一開始聲明命名空間--> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000"><!--圖形的座標系--> <defs><!--設置後面要用到的一些定義--> <linearGradient id="fade"><!--將一種漸變色命名爲"fade"--> <stop offset="0%"stop-color="#008"/><!--深藍--> <stop offset="100%"stop-color="#ccf"/><!--漸變到淺藍--> </linearGradient> </defs> <!--畫一個具備寬的黑色邊框而且漸變色爲填充色的矩形--> <rect x="100"y="200"width="800"height="600" stroke="black"stroke-width="25"fill="url(#fade)"/> </svg>
學習資料:
http://www.w3school.com.cn/svg/index.asp
https://www.w3.org/TR/
當使用<img>或者<object>元素展現SVG圖形的時候,SVG就變成了另一種圖片格式了,這種方式對於JavaScript程序員來講是不友好的。更好的方式是直接將SVG圖片嵌入到HTML文檔中,這樣這些圖片就能夠經過腳本的方式來控制。因爲SVG就是一種XML語法,所以能夠將它以以下的方式嵌入到XHTML文檔中:
//保存爲XHTML文件 <?xml version="1.0"?> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg"> <!--聲明HTML做爲默認的命名空間,以"svg:"前綴的爲SVG的命名空間--> <body> This is a red square:<svg:svg width="10" height="10"> <svg:rect x="0" y="0" width="10" height="10" fill="red"/> </svg:svg> This is a blue circle:<svg:svg width="10" height="10"> <svg:circle cx="5" cy="5" r="5" fill="blue"/> </svg:svg> </body> </html>
HTML5將XML和HTML的區別進一步縮小,容許SVG(和MathML)標記直接在HTML文件中使用,不須要命名空間的聲明或者標籤前綴:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> This is a red square:<svg width="10"height="10"> <rect x="0"y="0"width="10"height="10"fill="red"/> </svg> This is a blue circle:<svg width="10"height="10"> <circle cx="5"cy="5"r="5"fill="blue"/> </svg> </body> </html>
例21-2是一個pieChart()函數。
例21-3是另一個用腳本繪製SVG圖形的例子:它使用SVG來繪製一個模擬時鐘
大部分的畫布繪製API都不是在<canvas>元素自身上定義的,而是定義在一個「繪製上下文」對象上,獲取該對象能夠經過調用畫布的getContext()方法。調用getContext()方法時,傳遞一個"2d"參數,會得到一個CanvasRenderingContext2D對象,使用該對象能夠在畫布上繪製二維圖形。這裏很重要的一點是要搞清楚,畫布元素和它的上下文對象是兩個徹底不一樣的對象。因爲CanvasRenderingContext2D名字太長了,所以這裏作個約定,統一簡稱爲「上下文對象」。一樣地,「畫布API」指的也就是CanvasRenderingContext2D對象的方法。
畫布中的3D圖形
瀏覽器提供商實現<canvas>元素用於繪製3D圖形的API。這些API稱爲:"WebGL",它是綁定到OpenGL標準API的一個JavaScript。將"webgl"字符串做爲參數傳遞給畫布的getContext()方法能夠得到用於繪製3D圖形的上下文對象。因爲WebGL很龐大,並且也很是複雜,本書將不會介紹它的一些底層API:其實Web開發者也更傾向於使用封裝了WebGL底層API的工具類庫而不喜歡直接使用WebGL API。
示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> This is a red square:<canvas id="square"width=10 height=10></canvas>. This is a blue circle:<canvas id="circle"width=10 height=10></canvas>. <script> var canvas=document.getElementById("square");//獲取第一個畫布元素 var context=canvas.getContext("2d");//獲取2D繪製上下文 context.fillStyle="#f00";//設置填充色爲紅色 context.fillRect(0,0,10,10);//填充一個正方形 canvas=document.getElementById("circle");//第二個畫布元素 context=canvas.getContext("2d");//獲取它的繪製上下文 context.beginPath();//開始一條新的路徑 context.arc(5,5,5,0,2*Math.PI,true);//將圓形添加到該路徑中 context.fillStyle="#00f";//設置填充色爲藍色 context.fill();//填充路徑 </script> </body> </html>
以前咱們看到SVG使用能夠繪製或填充的線段和曲線這種路徑來描述複雜的圖形。畫布API也採用「路徑」的思想。然而不一樣的是,相比SVG使用一個包含了字母和數字的字符串來描述路徑,畫布API是經過一系列方法調用來定義路徑的,如上述代碼中的beginPath()和arc()方法調用。一旦定義了路徑,其餘的諸如fill()這樣的方法就能夠在該路徑上操做了。而像fillStyle這樣的上下文對象的屬性則是指定了如何進行這些操做。接下來的內容將解釋: