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这样的上下文对象的属性则是指定了如何进行这些操做。接下来的内容将解释: