HTML5 解决方案:面向 HTML5 开发者的基本技巧(二)
原文:HTML5 Solutions Essential Techniques for HTML5 Developers
五、HTML5 媒体元素:音频和视频
今天,网络真正走向了多媒体。音频和视频已经成为我们每天在网上浏览的内容不可或缺的一部分。由于多媒体内容的带宽和压缩技术的不断发展,现在在移动设备上观看电视节目或舒适地坐在电视机前观看电影已经很常见,这要归功于支持苹果或谷歌电视技术的设备。
此外,大多数电视网络在其网站上提供免费或收费的内容,更不用说 YouTube 和类似的视频分享网站的流行了。
尽管多媒体内容的可用性很高,但在 HTML5 之前,并没有在 Web 上传送多媒体的开放标准。事实上,多媒体内容在 Web 上的交付曾经(现在仍然)委托给第三方插件,如 QuickTime、Windows Media Player、Flash Player 和 Real Player。
一开始是一片混乱。插件的不均衡可用性导致那些想要在网站上发布视频的人以不同的格式提供视频,这些格式可以在最常见的播放器上使用。
多种格式意味着在网站上发布时使用不同的视频编解码器对同一视频进行编码。视频编解码器是能够对数字视频进行视频压缩和/或解压缩的软件。压缩通常采用有损数据压缩方法。“有损”是指软件通过丢失原始源的一些信息来压缩数据。在维基百科上阅读更多关于视频编解码器的信息:[en.wikipedia.org/wiki/Video_codec](http://en.wikipedia.org/wiki/Video_codec)。
这是插件之间的一场真正的战争,在 2005 年到 2006 年间,一个插件脱颖而出:Adobe Flash Player。
Adobe Flash Player 是一种插件,在早年非常流行,90%的联网机器都安装了这种插件。在被 Adobe 收购之前,Macromedia 在 Flash Player 7 中插入了 Flash 视频格式,没过多久,业内的大公司就采用了这种格式,这样几乎任何人都可以播放基于 Flash 的视频。
Flash Video 是一种容器文件格式,用于使用 Adobe Flash Player 版本 6–10 通过互联网传送视频。Flash 视频内容也可以嵌入到 SWF 文件中。
有两种不同的视频文件格式称为 Flash 视频:FLV 和 F4V。FLV 文件中的音频和视频数据的编码方式与 SWF 文件中的相同。Adobe Flash Player 支持这两种格式,它们目前由 Adobe Systems 开发。FLV 最初是由 Macromedia 开发的。
这种格式已经迅速成为网络上嵌入式视频的首选格式。Flash 视频格式的著名用户包括 YouTube、Hulu、Google Video、Yahoo!视频、Metacafe、Reuters.com 和许多其他新闻提供商。
注意:虽然 Flash Player 和 Flash Video 格式非常受欢迎和支持,但 Apple 决定不支持 iOS 设备(iPhone、iPad 和 iPod Touch)使用它。相反,这些设备支持 HTML5。
如果你想进一步研究这个话题,可以从苹果公司首席执行官在[www.apple.com/hotnews/thoughts-on-flash/](http://www.apple.com/hotnews/thoughts-on-flash/)发表的“关于 Flash 的想法”和 Adobe 首席执行官在[blogs.wsj.com](http://blogs.wsj.com)/digits/2010/04/29/live-blogging-the-journals-interview-with-adobe-ceo/的回复开始。
有关 Flash 视频的更多信息,请访问维基百科:[en.wikipedia.org/wiki/Flash_Video](http://en.wikipedia.org/wiki/Flash_Video)。
在这个非凡的发展时期,HTML 提供了两个标签来允许人们在网页中插入外部插件可以使用的内容:标签<object>和标签<embed>。
HTML5 引入了一种用于传送多媒体内容的本地开放标准。因为语言规范仍在定义中,所以只在表 5-1 中列出的浏览器版本中支持这些新标签。
当前的问题不是采用 HTML5 引入的新标记,因为 HTML5 是编解码器使用的标准定义(见图 5-1 )。相反,问题是 Ogg Theora、H.264 和 VP8/WebM 是目前视频元素支持的唯一格式。其中哪些会在未来得到浏览器的普遍支持?这个问题我们还没有答案。
目前在这个问题上有很大的争论和不同的观点。讨论最多的是谷歌的立场,谷歌收购了 On2 Technologies,并开源了 VP8/WebM 视频编解码器。2011 年 1 月,谷歌也宣布将从 Chrome 上移除对 H.264 编解码器的支持,但苹果和微软仍将继续支持这一编解码器。
注:On2 Technologies 生产以下视频编解码器:VP3、VP4、VP5、VP6、VP7 和 VP8。Ogg Theora 源自 On2 Technologies 向公众发布的专有 VP3 编解码器。
本质上,一场建立下一个视频编解码器的新战役已经开始。和往常一样,我们只能拭目以待。
图 5-1 网络上视频编解码器的现状及其浏览器支持。(*来源:*维基百科,[en.wikipedia.org/wiki/HTML5_video](http://en.wikipedia.org/wiki/HTML5_video))
在本章中,我们将介绍如何使用新的 HTML5 标记来处理音频和视频多媒体内容的解决方案。
解决方案 5-1:在网页中嵌入视频
有了 HTML5,在网页上发布视频变成了真正简单的操作。我们借助第三方插件让视频在 HTML 页面中可访问的时代即将结束。
像这样的代码可能会成为遥远的记忆:
`
`最终用户将不再需要为了观看视频而下载额外的插件或者更新到他们已经安装的插件的正确版本。也不会因为某些第三方插件的不稳定性而导致浏览器崩溃。
让我们看看 HTML5 的变化。
涉及到什么
随着新的<video>标签的引入,我们所要做的就是在网页中声明这个标记,指定要加载的视频,浏览器将完成剩下的工作(假设它支持视频元素):
<video src="your_video.ogg" />
src属性包含要在页面中显示的媒体资源(视频或音频)的地址。在上面的代码示例中,我们要求它加载 Ogg Theora 格式的视频。
即使该代码本身足以使视频可用,也最好使用宽度和高度属性来指定视频容器的尺寸:
<video width="640" height="360" src="your_video.mp4" />
如果不设置这些值,浏览器将使用原始视频资源的尺寸。
视频标签支持的其他属性包括:
- preload: 告诉浏览器在页面加载时预加载视频内容。这样,用户在播放视频时就不必等待视频加载。
- *自动播放:*告诉浏览器一旦视频可用就自动播放。您需要小心使用这个属性,因为您并不总是确定用户是否想看视频。如果用户通过移动设备连接,这一点尤其正确,因为带宽更贵。
- *循环:*视频一结束就重新执行。
- *控件:*如果指定,它告诉浏览器显示一组内置控件,如播放、停止、暂停和音量。
- poster: 指定用户代理(浏览器)在没有视频数据可用时可以显示的图像文件。
如何建造它
下面的代码示例演示如何在网页中导入和显示视频:
`
Solution 5-1: Embedding a video in a web page
Comtaste's Showreel
<video width="640" height="360"
src="comtaste_showreel.mp4" autoplay />
`
如果在浏览器中打开这个例子,视频会立即执行,占据 640 像素宽,800 像素高的空间,如图图 5-2 所示。
**图 5-2。**视频会在网页中自动播放。
注意:如果您想尝试这个加载远程视频的解决方案,您可以使用下面的代码:
*<video width="640" height="360" src="http://www.youtube.com/demo/ google_main.mp4" autoplay />*
如果用户在不支持视频标签的浏览器中打开 HTML 文件,将显示一个空白页面。因此,在发生这种情况时,最好在页面中提供替代内容。使用 poster 属性显示可能是视频帧的图像(作为图像捕获)。它可以是本地的,也可以来自网络上的其他地方。
让我们将该属性添加到视频标签中:
<video width="640" height="360" src="comtaste_showreel.mp4" autoplay poster="../img/Figure_5_3.png"> Video is not supported in this browser! </video>
如果视频没有加载,浏览器会显示海报属性中指定的图像,如图图 5-3 所示。
**图 5-3。**浏览器显示海报属性中指定的图像。
如果未指定 poster 属性,并且浏览器无法加载视频,则默认情况下将显示电影的第一帧。
专家提示
对于移动 Apple iOS 设备(iPhones、iPod Touches 和 ipad)和 Android 设备,HTML5 视频支持存在一些问题:
- 如果使用 poster 属性,iOS 将忽略视频元素。苹果已经声明在 iOS 4.0 中修复了这个 bug。
- iOS 只支持 H.264 格式。如果使用
<source>标签(见下一个解决方案),它只会识别第一种视频格式。 - 另一方面,Android 设备不支持浏览器的本地控件,因此会忽略它们。此外,操作系统会对用于指定视频容器的 type 属性感到有点困惑。
解决方案 5-2:检测跨浏览器的视频支持
在这一章的前面,我们已经讨论了网络上可用的各种视频格式。
HTML5 的第一个规范规定浏览器对视频元素的支持必须基于两种格式:Ogg Vorbis(用于音频)和 Ogg Theora(用于视频)。这一声明在苹果和诺基亚等巨头中引起了不小的轰动,以至于 WSC 从规范中删除了对音频和视频格式的任何引用。
这个选择显然导致了网络上的格式混乱。MPEG4 (H.264)、Ogg Theora、AVI 和 VP8/WebM 都准备发动战争,希望成为未来的标准。就连浏览器也开始站队了。图 5-4 总结了各种浏览器对视频容器的支持:
- Opera 支持 Ogg Theora,未来还会支持 WebM。
- Chrome 支持 Ogg Theora 和 WebM。(它最近宣布不再支持 H.264。)
- Firefox 支持 Ogg Theora,未来还会支持 WebM。
- Safari 支持 H.264。
- Internet Explorer 支持 H.264 和 WebM。
**图 5-4。**浏览器对视频容器的支持
在当前情况下,很容易理解为什么有必要进行检查来验证加载页面的浏览器,并选择要加载的视频格式的正确版本。
涉及到什么
有多种技术可以验证加载页面的浏览器支持哪种视频格式。这个解决方案使用了来自[www.modernizr.com](http://www.modernizr.com)的 Modernizr JavaScript 库,我们在第一章中介绍过。
我们还可以使用这个视频库的本地函数来验证它对视频标签和编解码器的支持。这里有一个例子:
if (Modernizr.video) { if (Modernizr.video.webm) { // Support the WebM } else if (Modernizr.video.ogg) { // Support the Ogg Theora + Vorbis } else if (Modernizr.video.h264){ // Support the H.264 video + AAC audio } }
基本上,该库通过测试视频元素的canPlayType属性来执行视频支持验证。
然后,浏览器返回三个值之一:
- *空字符串:*不支持。
- *可能是字符串:*不确定不支持。
- *大概字符串:*支持容器和编解码器的组合。
如何建造它
要创建一个验证视频标签支持并加载正确代码的 HTML 页面,请使用一种与前面的解决方案类似的机制,但有一些小的不同。
以下是使用 Modernizr 库检测跨浏览器视频支持的完整代码:
`
Solution 5-2: Detecting Video support across browserbody { background: #f7f7f7; color: black; text-align: center; } .video #no-video, .no-video #video { display: none; }
Comtaste's Showreel
This is an HTML5 Video Element
注意:在撰写本文时,Modernizr 库的最新版本是 1.7,但是这个解决方案也适用于旧版本。
我们在代码中创建了两个 div:一个包含支持<video>标签的浏览器的代码(id 等于#video),另一个包含其他浏览器的代码(id 等于#no-video)。我们还插入了一个带有 CSS 语句的样式块,以隐藏带有命令 display: none的必要 DIV。
在第一个 DIV(id # video)块中,我们插入了<video>标签,而在第二个块中,我们将使用 Kroc Camen 编写的技术,它不使用 JavaScript,只需要两种视频编码:一个 Ogg 文件和一个 MP4 文件。
这种技术基于一种假设,即如果不支持 HTML5 视频,将使用 Adobe Flash 来代替。
这种方法与 HTML4、HTML5(有效标记)和 XHTML 1 兼容,当用作 application/XHTML+XML 时也能工作。
在 Kroc Camen 的“人人视频”中,可以了解更多关于这种使用 HTML5 视频的无 JavaScript 方法的信息。你可以在[camendesign.com/code/video_for_everybody](http://camendesign.com/code/video_for_everybody)找到这篇文章。
在第二个 DIV(如果浏览器不支持 video 标记,将加载该 DIV)中,插入以下代码:
<!-- fallback to Flash: --> <object width="640" height="360" type="application/x-shockwave-flash" data="__FLASH__.SWF"> <!-- Firefox uses the data attribute above, IE/Safari uses the param below --> <param name="movie" value="comtaste_showreel.SWF" /> <param name="flashvars" value="controlbar=over&image= comtaste_showreel.JPG&file=comtaste_showreel.mp4" /> <!-- fallback image. note the title field below, put the title of the video there --> <img src="comtaste_showreel.JPG" width="640" height="360" title="No video playback capabilities, please download the video below" /> </object>
专家提示
对于如何处理网络上的视频,还有其他的解决方案。在[github.com/etianen/html5media](https://github.com/etianen/html5media)有一个非常成功的项目是 HTML5media 项目。
这个项目通过使用 HTML5 多媒体播放器来检测对视频标签和视频格式的双重支持。它支持 H.264 (MP4)和 Ogg Theora 格式。
如果浏览器不支持 HTML5 视频标签,它会使用 Adobe Flash Player 来提供与原始视频相同的功能。这也是我们使用 Flowplayer JavaScript 库([flowplayer.org](http://flowplayer.org)))的原因。
要在所有主流浏览器中启用 HTML5 视频标签,您只需调用 jQuery 库和文档头中的脚本:
`
`然后,您可以使用以下代码将视频插入到 HTML 页面中:
<video src="video.mp4" autoplay autobuffer></video>
其他相同主题的库有:[mediaelementjs.com/](http://mediaelementjs.com/)和[videojs.com/](http://videojs.com/)。
解决方案 5-3:创建自定义视频控制器
任何多媒体元素,无论是音频还是视频,都必须为用户提供使用传统的播放、停止、暂停和音量控制按钮与内容进行交互的选项。HTML5 有一个在屏幕视频控制器上本地呈现上述按钮的属性。
但是,创建自定义控件按钮是可能的(通常也是更可取的),以便它们与发布视频的网站的图形相匹配。
音频和视频元素是 HTML5 DOM 媒体元素的一部分,它提供了一个强大且非常易于使用的 API 来控制电影回放。在本解决方案中,我们将了解如何向视频添加自定义控件。
涉及到什么
在视频内容中插入一个视频控件很简单。事实上,<video>标签有一个 controls 属性,它利用了浏览器的内置控件。您需要做的只是在标签中指定属性,以在视频上显示控制器:
<video width="640" height="360" src="comtaste_showreel.mp4" controls />
每个浏览器都将使用自己的图形来设计视频控件。例如,在图 5-5 中,您可以看到 Safari 中用于视频控制的图形。
**图 5-5。**Safari 渲染的视频控件
这种差异会让许多设计师感到惊讶,因为他们想要控制视频控件的外观和感觉,使它们与网站的图形相匹配。
在图 5-6 中,你可以看到所有其他主流浏览器中用于视频控件的图形。
**图 5-6。**主流浏览器如何渲染视频控件
但是,您可以使用自己的图形创建自定义视频控件,并决定提供哪些功能。例如,您可以编写一个功能来同步视频中的字幕并用视频控制器激活它们,或者编写一个功能来允许用户从一个书签跳到另一个书签,以查看他或她最感兴趣的片段。
因为你是用 JavaScript 写视频控制器功能的人,所以真的可以随心所欲。
为此,您必须使用 HTML5 媒体属性和您可以监听的 DOM 事件,例如加载进度、媒体播放、媒体暂停和媒体播放完成。例如,对于播放视频的功能,有以下属性:
- media.paused: 如果回放暂停,则返回 true 否则为假。
- media.ended: 如果回放已经到达媒体资源的结尾,则返回 true。
- *media . defaultplaybackrate[= value]:*返回当用户不在媒体资源中快进或倒退时的默认回放速率。
- 媒体。playbackRate [ = value ]: 返回当前播放速率,其中 1.0 为正常速度。
- Media.played: 返回一个
TimeRanges对象,代表浏览器已经播放的媒体资源的范围。 - Media.play(): 将 media.paused 属性设置为 false,加载媒体资源并在必要时开始播放。如果播放已经结束,它将从头开始播放。
- Media.pause(): 将 media.paused 属性设置为 true,在必要时加载媒体资源。
- Media.volume: 获取或设置视频音轨的音量。它采用范围从 0.0(无声)到 1.0(最大声)的浮点值。
- Media.muted: 使视频静音。
- Media.currentTime: 以秒为单位返回当前播放位置,用浮点数表示。
有关媒体属性和事件的完整列表,请参阅下页:[www.whatwg.org/specs/web-a…](http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html)。
通过使用这些方法和几行 JavaScript,您可以创建一个简单的视频控制器:
if (video.paused) { video.play(); }
这个条件控制视频是否暂停,如果暂停,它用play()方法执行视频。另一方面,如果视频已经结束,我们必须通过操作currentTime属性回到开始,然后再次播放它:
if (video.paused) { video.play(); } else if (video.ended) { video.currentTime=0; video.play(); }
基本上,契约是用几行代码完成的。让我们看看如何创建一个完整的示例。
如何建造它
首先创建两个 DIV 块。第一个将包含视频元素,第二个将包含视频控件。
然后创建一个新文件,并在一个 DIV 块中添加一个<video>标记,并为其分配一个等于video_container的 id。id 很重要,因为稍后您将使用 JavaScript 和 CSS 访问它。
以下是代码片段:
`
它加载一个 MPEG 格式的视频,大小为 320×176 像素。
插入第二个 DIV 块,它将包含用于播放、暂停、音量、静音和计时的视频控件。为此,请为播放、暂停和静音按钮使用<button>标签,然后使用滑块来控制音量。关于视频播放时间的信息包含在一个标签中:
`
Play
Pause
Mute
-:--:--
在这种情况下,注意标记的 id 属性的名称也很重要,因为稍后您将需要它们来引用 JavaScript 中的对象。
网页用户界面的最终结果如图图 5-7 所示。
**图 5-7。**视频元素及其自定义视频控制器
现在你要写视频控制器的逻辑。插入一个脚本块,并在网页加载后立即启动事件处理程序。在事件处理程序中,定义包含视频元素的全局变量和包含视频控制器引用的局部变量:
`
var video;
window.onload = function(){
video = document.getElementsByTagName("video")[0];
var btn_play = document.getElementById("btn_play");
var btn_pause = document.getElementById("btn_pause");
var btn_mute = document.getElementById("btn_mute");
var btn_volume = document.getElementById('volume');`
我们使用了getElementsByTagName方法来访问视频元素,使用getElementByI d 方法来访问具有指定 id 的第一个元素,以引用其他控件。这样,我们可以访问引用视频全局变量的视频元素,而不必调用getElementById方法。
对于在视频上执行操作的按钮,您必须将它们的 click 事件关联到一个事件处理程序(实际上是一个方法),您可以在其中编写控制按钮必须执行的操作。我们使用addEventListener()方法将一个元素的事件关联到一个方法。
首先,将以下代码行添加到代码中。(我们还在 window 对象的 onload 事件中写代码。)
` btn_play.addEventListener('click', doPlay, false);
btn_pause.addEventListener('click', doPause, false);
btn_mute.addEventListener('click', doMute, false);`
三个事件处理程序与按钮的点击相关联:doPlay()、doPause()和doMute()。
最后,在关闭页面的 onload 事件中的语句之前,添加一个事件处理程序来处理将对 slider 控件的 change 事件起作用的视频元素的音量。
在其余代码之后直接添加以下代码:
`btn_volume.value = video.volume; btn_volume.addEventListener('change',function(e) {
myVol= e.target.value;
video.volume=myVol;
if (myVol==0) {
video.muted = true; } else {
video.muted = false;
} return false; }, true); };`
让我们分析一下刚刚编写的代码。当您打开网页时,视频元素将从用户设置中继承音量。这就是为什么您必须将滑块控件的值设置为视频的当前值:
btn_volume.value = video.volume;
这样,您就可以确定用户会从视频的实际初始值开始改变视频的音量。
然后,我们为 change 事件创建了一个事件处理程序,该事件在用户拖动滑块时发生。这是用户更改视频元素音量的方式,它将等于滑块的值:
myVol= e.target.value; video.volume=myVol;
我们执行一个条件来检查体积是否等于 0。此设置设置视频的静音属性;否则,音量改变时会自动取消静音:
if (myVol==0) { video.muted = true; } else { video.muted = false; }
通过这几行代码,您已经为视频元素编写了音量控制函数。
现在我们可以开始为按钮编写事件处理程序了。添加以下代码:
`function doPlay(){ if (video.paused){ video.play(); } else if (video.ended) { video.currentTime=0; video.play(); }; };
function doPause(){
if (video.play){
video.pause();
};
};
function doMute(){
document.getElementById('volume').value = 0; video.muted = true;
};`
通过play()和pause()方法以及 muted 属性,我们提供了视频控制器按钮的功能。
现在,您可以保存文件并在浏览器中执行它。您将看到播放、暂停和静音按钮是如何工作的,您还可以通过移动滑块来改变视频的音量。
这是该示例的完整代码:
`
Solution 5-3: Creating custom video controlsPlay
Pause
Mute
专家提示
video 元素公开了一个事件,该事件允许您跟踪视频执行的剩余时间。
当当前回放位置作为正常回放的一部分改变时,执行timeupdate事件。
让我们添加一个标记标签,它将包含视频播放时的计时。
在 DIV 块中插入以下代码:
` Play
Pause
Mute
<label id="time">-:--:--
`我们将使用 JavaScript 在标签中插入视频执行小时、分钟和秒的值。
为视频元素的timeupdate事件添加一个事件处理程序。在 window 对象的 onload 事件的函数中插入以下代码行:
video.addEventListener('timeupdate', updateTime, false);
然后声明updateTime方法,该方法将小时/分钟/秒值写入我们在网页视图中创建的<label>中:
`function updateTime() { var sec= video.currentTime; var h = Math.floor(sec/3600); sec=sec%3600; var min =Math.floor(sec/60); sec = Math.floor(sec%60);
if (sec.toString().length < 2) sec="0"+sec; if (min.toString().length < 2) min="0"+min;
document.getElementById('time').innerHTML = h+":"+min+":"+sec;
}`
该函数的唯一任务是将currentTime(以秒为单位)分割成单独的小时/分钟/秒字符串,然后通过innerHTML方法将其写入 id 等于时间的标签中:
document.getElementById('time').innerHTML = h+":"+min+":"+sec;
然后添加一点 CSS 来使标签字段更好看:
` #video_container { margin: 0; padding: 0; }
#time { margin: 0; padding: 5px; width: 350px; font-family: Helvetica, Arial, sans-serif font-size: .7em; color: #000000; background-color: #ccc; }
`
最终结果如图图 5-8 所示。
**图 5-8。**视频元素及其工作视频控制器
解决方案 5-4:预加载视频
通过互联网传送媒体有两种主要方法:
- 流动
- 渐进式下载
对这两种方法之间差异的讨论超出了本解决方案的范围。我们需要知道的是,第一种方法,即流式传输,使用一个服务器和一个协议来允许用户在整个视频长度内查看视频的任何部分,而不必等待视频加载。
另一方面,第二种方法使用 HTTP 标准协议来传输文件。在这种情况下,用户不能跳到尚未加载的视频部分。
因此,对于以渐进式下载方式交付的视频,管理预加载操作非常重要。这允许你在后台加载视频,即使用户还没有执行电影。
涉及到什么
video 元素有一个 preload 属性,允许您在后台加载视频。您只需在 video 标记中声明它,并指定它可以具有的三个值之一:
- *auto(默认值):*页面一加载就开始下载视频文件。
- *无:*不开始下载视频文件。
- 元数据:建议浏览器预取资源元数据,如大小、持续时间等。
然而,你必须非常注意如何使用这个属性。事实上,这个属性告诉浏览器在页面加载时就开始下载视频文件。即使用户从不执行视频,在后台页面也会要求加载,浪费了宝贵的带宽。
下面是如何使用属性的示例:
<video width="640" height="360" src="comtaste_showreel.mp4" autoplay preload poster="../img/Figure_5_3.png" />
注意,iOS 上的 Safari 浏览器会忽略 preload 属性,从不预加载视频。
如何建造它
为此解决方案创建一个新文件,从插入要预加载的视频开始:
`
Solution 5-4: Preloading a video<video width="320" height="176" preload src="comtaste_showreel.mp4" />
因为 auto 值是默认值,所以不必写 preload="auto "。
然而,在本例中,我们指定浏览器根本不应该预加载视频:
<video width="640" height="360" src="comtaste_showreel.mp4" autoplay preload="none" poster="../img/Figure_5_3.png" />
专家提示
有些浏览器支持autobuffer属性,在不使用autoplay时使用,它会强制在后台下载视频。iOS 上的 Safari 浏览器忽略预加载属性,从不预加载视频。如果autoplay和autobuffer都被使用,那么autobuffer被忽略。
由于autobuffer属性在 Firefox 4 中不再存在,并且preload属性在 Firefox 3.5 和 3.6 中也不存在,如果您想要完整下载一个视频,您需要在 video 元素中同时使用preload attribute set to "auto"和autobuffer:
<video autobuffer preload="auto" width="640" height="360" src="comtaste_showreel.mp4" />
请注意这种方法,因为即使用户永远看不到视频,也会下载视频,从而耗尽带宽。如果您希望视频仅在用户实际播放时才被下载,您必须省略preload或autobuffer属性。
解决方案 5-5:为视频创建自定义搜索栏
每个像样的视频控制器还提供了通过搜索栏跳到电影中特定点的选项,以及管理音量、播放和暂停视频的功能。
YouTube 允许用户通过在视频控制器的播放和暂停按钮上拖动拇指,使用搜索栏快速跳过视频,如图图 5-9 所示。
**图 5-9。**YouTube 使用的搜索栏
通过使用 video 元素的一些属性并编写几行 JavaScript,也可以用 HTML5 创建这种类型的函数。
涉及到什么
我们将使用两个属性和一个事件来创建一个搜索栏:currentTime和duration属性,以及timeupdate事件。
我们已经在之前的解决方案中讨论过这些属性,但这里有一个快速提醒:
- 属性返回当前时间,以秒为单位,作为一个浮点值。
duration属性以浮点值的形式返回以秒为单位的实际持续时间。如果持续时间未知,则返回 NaN 如果视频是流式的,则返回 Infinite- 无论当前位置如何变化,都会执行
timeupdate事件。
通过将这些属性与滑块控件相关联,您可以为用户提供从视频的一部分跳到另一部分的选项。
对于滑块控件,使用 HTML5 的新输入类型range,它使用浏览器的内置特性:
<input type="range" step="any" id="seekbar">
当timeupdate事件被执行时,这个输入元素将更新它的值,因为它将被绑定到视频的时间值。
让我们看看它是如何工作的。
如何建造它
对于这个解决方案,让我们以我们在前一个解决方案中创建的示例为例,您将向其中添加一个滑块控件,该控件将充当视频元素的搜索栏。
首先添加本例中唯一的新用户界面元素:slider。
在 id 等于video_controller的 DIV 块中,在静音按钮和包含视频时序的标签之间添加一个range输入类型:
`
Play
Pause
Mute
-:--:--
我们还没有处理max属性,该属性允许我们为这个 input 元素的 slider 控件指定最高值。我们将使用 JavaScript 为这个属性赋值。
但是首先,让我们处理代码的脚本块。
首先,将下面几行 JavaScript 代码添加到对象窗口的 onload 事件中。
使用 document 对象的getElementById方法创建一个包含滑块引用的局部变量:
var seekbar = document.getElementById('seekbar');
在 slider 控件的 change 事件和 video 元素的timeupdate和durationchange上声明三个事件处理程序:
video.addEventListener('timeupdate', updateTime, false); video.addEventListener('durationchange', initSeekBar, false); seekbar.addEventListener('change', changeTime, false);
因此,onload 事件上的函数如下所示:
`window.onload = function(){
video = document.getElementsByTagName("video")[0]; var btn_play = document.getElementById("btn_play");
var btn_pause = document.getElementById("btn_pause");
var btn_mute = document.getElementById("btn_mute");
var seekbar = document.getElementById('seekbar');
btn_play.addEventListener('click', doPlay, false);
btn_pause.addEventListener('click', doPause, false);
btn_mute.addEventListener('click', doMute, false);
video.addEventListener('timeupdate', updateTime, false);
video.addEventListener('durationchange', initSeekBar, false);
seekbar.addEventListener('change', changeTime, false);`
现在让我们通过将滑块的min和max属性与视频元素的startTime和duration属性相关联来设置它们。特别是,将min属性设置为 0,将max属性设置为duration。我们将此语句写在initSeekBar()事件处理程序上,当执行视频的durationchange事件时会调用该处理程序:
function initSeekBar() { seekbar.min = 0; seekbar.max = video.duration; }
为了确保当用户移动滑块的拇指时视频改变其定时,并且搜索栏与视频的定时同步,我们必须在事件处理程序上编写代码,以便它们分别在视频的updateTime事件和滑块控件的change事件上执行。我们用addEventListener方法注册了具有以下功能的事件处理程序:
seekbar.addEventListener('change', changeTime, false); video.addEventListener('timeupdate', updateTime, false);
在第一个视频的名为updateTime()的事件处理程序中,我们将滑块控件的值与视频的currentTime相关联:
function updateTime() { seekbar.value = video.currentTime; }
而在第二个事件处理程序changeTime上,我们做了相反的事情,这意味着我们将视频的currentTime与滑块的值相关联:
function changeTime() { video.currentTime = seekbar.value; }
保存文件并在浏览器中打开。你会看到滑块控件与视频完全同步,如果你拖动滑块,你会看到视频的当前时间相应改变,如图 5-10 所示。
**图 5-10。**搜索栏与视频元素的当前时间完美同步。
为了清楚起见,下面是这个例子的完整代码:
`
Solution 5-5: Browsing the video with a seek bar #video_container { margin: 0; padding: 0; } #time { margin: 0; padding: 5px; width: 350px; font-family: Helvetica, Arial, sans-serif font-size: .7em; color: #000000; background-color: #ccc;` `}<video width="320" height="176" src="comtaste_showreel.mp4" />
Play
Pause
Mute
-:--:--
专家提示
有一种情况,上面写的例子不能正确工作。事实上,如果视频是流式的,duration属性将变成无穷大,视频将从等于 0 的startTime开始执行。因此,我们必须更改代码,使其在这种情况下也能正常工作。
视频的startTime属性来拯救。它通常返回 0,但对于流式视频,它可能会返回不同的值。
因此,我们必须在代码中做两处修改。第一个是在initSeekBar()事件处理程序中,与min和max属性有关:
function initSeekBar() { seekbar.min = video.startTime; seekbar.max = video.startTime + video.duration; }
基本上,min属性的值不再为零;相反,它继承包含在startTime属性中的值,而max值等于startTime加上包含在duration属性中的值。
第二个变化是在事件处理程序updateTime()中,我们将使用另一个属性:buffered属性。它返回一个包含一个length属性、start()和end()方法的TimeRanges对象。
我们通过访问buffered属性中最后一个范围的结束时间来获取缓冲数据的位置。
如下更改updateTime()处理程序中的代码:
`function updateTime() { var bufferPosition = video.buffered.end(video.buffered.length-1); seekbar.min = video.startTime; seekbar.max = bufferPosition; seekbar.value = video.currentTime;
}`
解决方案 5-6:使用多源视频元素
当前浏览器对各种视频容器和格式的支持如下:
- Safari 可以读取任何兼容 H.264/AAC/MP4 格式的视频。
- 微软还没有说明对 WebM 的支持是原生的,还是在 Internet Explorer 中可单独安装的组件中提供。在任何情况下,它都支持 H.264/AAC/MP4 格式。
- Google 支持 WebM 编解码器,它已经宣布未来版本的 Android 将支持 WebM 格式。它最近还表示,截至 2011 年 3 月,Chrome 将不再支持 H.264 编解码器。
- 火狐、Opera、Chrome 都支持 Ogg/Theora/Vorbis 格式。
在这种丰富多彩的场景中,有必要以不同的格式发布视频,以便能够保证尽可能多的用户使用视频。
然后将由浏览器来理解它能够再现这些格式中的哪一种。
涉及到什么
这些步骤将帮助您提高视频被用户执行的可能性:
- 以下列格式编码您的视频:在 MP4 容器中编码 WebM (VP8 + Vorbis)、H.264 视频和 AAC 音频,在 Ogg 容器中编码 Theora 视频和 Vorbis 音频。
- 从一个
<video>元素声明这三个视频文件,并退回到一个基于 Flash 的视频播放器(参见解决方案 5-1 )。
为了使浏览器能够理解它需要加载哪种视频格式,可以使用 source 元素,如下所示:
<video controls autoplay> <source src="comtaste_showreel.ogv"> <source src="comtaste_showreel .mp4"> Sorry, your browser does not support the Video element </video>
浏览器将读取第一个视频源,并尝试加载和播放comtaste_showreel.ogv。如果它不能播放,它将尝试下一个源元素。
默认情况下,浏览器还会试图以一种它无法再现的格式加载视频。这将是对带宽的巨大浪费。为了节省宝贵的带宽,您可以指定每个视频的 MIME 类型。MIME 类型传达正在使用哪个视频容器,但是它不提供关于正在使用的编解码器的任何信息。因此,在源的type属性中,还需要指定编解码器。
通过这样做,浏览器不需要下载视频,因为它知道是否支持该 MIME 类型:
<video controls> <source src="comtaste_showreel.ogv" type="video/ogg;codecs='theora, vorbis'"> <source src="comtaste_showreel.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2”'> Sorry, your browser does not support the Video element </video>
如何建造它
下面是一个完整的代码示例,它使用 source 标记向浏览器公开不同的视频格式:
`
Solution 5-7: Using multiple source video elements
` ` Your browser does not support the new HTML5 video element
`
注意使用双引号的编解码器参数。这意味着属性值必须使用单引号:
<source src="comtaste_showreel.webm" type='video/webm; codecs="vorbis,vp8"' />
专家提示
使用移动设备进行导航正变得越来越流行。越来越多的 web 开发人员使用 CSS 优化他们的设计,这样他们的网站就可以在比标准笔记本电脑屏幕分辨率更低的设备上使用。因此,视频提供商也有必要考虑谁将在手持设备上使用他们的内容,因为更高的质量可能是不可取的功能-就加载视频所消耗的带宽而言。
HTML5 提供了media属性,该属性在 video 元素的source属性中声明。该属性向浏览器询问媒体将被再现的屏幕尺寸。根据响应,视频标签能够发送针对不同屏幕分辨率优化的不同文件:
<video controls autoplay > <source src="comtaste_showreel_mobile.ogv" type='video/ogg;codecs="theora, vorbis"' media="(min-device-width:480px)">
这样,浏览器会被询问最小设备宽度是否为 480px。如果是,那么它会加载在编码阶段针对移动设备优化的comtaste_showreel_mobile.ogv视频。
注意:你可以在[www.whatwg.org/specs/web-a…](http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html)阅读更多关于media属性的内容。
解决方案 5-7:全屏打开视频
近年来,网络上的视频质量有了显著提高。由于高性能带宽的可用性不断增加,今天多媒体内容正在互联网上传播,质量与电视相当。
YouTube 已经列出了大量高清质量的视频,如图 5-11 所示。
图 5-11。 YouTube 允许你选择想要加载的视频质量。
有了这种质量,就可以全屏观看视频,就像我们在笔记本电脑上观看视频一样,只是现在我们是在网上观看这些视频。
Flash Player 的几个版本已经提供了以全屏模式再现视频的可能性。HTML5 和它的 video 元素也提供了获得相同效果的方法。
涉及到什么
一些浏览器已经在视频控制器上提供了一个按钮来原生切换到全屏,例如 Safari 5.0 和 iOS 3.2 及更高版本。另一方面,其他浏览器通过右键菜单来支持全屏模式,如 Firefox 3.6。
您可以使用webkitSupportsFullscreen布尔属性验证当前视频是否能够以全屏模式播放,并且您可以使用webkitEnterFullscreen()方法以全屏模式启动视频。您要做的第一件事是检查浏览器是否支持全屏模式:
if (video.webkitSupportsFullscreen) { }
然后执行视频元素的webkitEnterFullscreen()方法:
video.webkitEnterFullscreen();
若要退出视频的全屏模式,请按 Esc 键返回到普通视图,就像使用 Flash Player 一样。
如何建造它
创建全屏视频功能非常容易。
首先创建用户界面元素,在我们的示例中,它只是一个视频元素和一个简单的按钮:
`
Go Fullscreen `
加载页面时,该按钮将不可见。事实上,指定一个 CSS 元素使按钮不可见:
style="visibility:hidden".
只有在检查了浏览器支持该功能后,您才能使该按钮可见。
现在,您可以插入一个 JavaScript 块,将事件处理程序与以全屏模式启动视频的按钮的单击相关联,但前提是检查浏览器是否支持该功能。当执行视频的loadedmetadata事件时,或者当浏览器知道媒体资源的持续时间和尺寸时,执行该检查。事实上,webkitSupportsFullscreen属性在电影元数据完全加载之前是无效的。
在 window 对象的 onload 事件中编写以下语句:
`
window.onload = function() { video = document.getElementById("myVideo");
btn_fullscreen.addEventListener("click", forceFullScreen, false);
video.addEventListener("loadedmetadata", makeVisible, false); }`
现在您已经准备好编写 addEventListener()方法中声明的事件处理程序:forceFullScreen()和makeVisible()。
以下是该示例的完整代码:
`
Solution 5-8: Full-screen video ` `btn_fullscreen.addEventListener("click", forceFullScreen, false);
video.addEventListener("loadedmetadata", makeVisible, false); }
function makeVisible() { if (video.webkitSupportsFullscreen) { document.getElementById("btn_fullscreen").style.visibility="visible"; } }
function forceFullScreen() { video.webkitEnterFullscreen(); }
<video width="320" height="176" src="comtaste_showreel.mp4" id="myVideo" autoplay />
<button type="button" id="btn_fullscreen" style="visibility:hidden"> Go Fullscreen
`图 5-12 显示了点击全屏按钮后的全屏视频。
**图 5-12。**全屏模式下的视频。
专家提示
如果你想知道哪些视频播放器支持全屏模式,可以访问这个网页:[praegnanz.de/html5video/](http://praegnanz.de/html5video/)。
解决方案 5-8:对视频应用遮罩
你经常会看到视频出现在网络上非矩形或不连续的区域。沃达丰出版了这种情况的最早的例子之一,后来变得很有名;视频展示了在未来手环中播放的电影(见图 5-13 )。
**图 5-13。**沃达丰视觉手环中播放的蒙面视频
视觉效果引人注目,并利用了新的 Flash Player 功能,该功能支持视频的 alpha 通道。alpha 通道只不过是充当视频蒙版的图像。这个特殊的图像定义了视频出现的唯一活动区域。
通过使用基于 WebKit 引擎的浏览器的特定属性并应用图像作为遮罩,也可以用 HTML5 重新创建类似的效果。
WebKit ( [webkit.org/](http://webkit.org/))是一个开源的网页浏览器引擎。WebKit 也是 Safari、Dashboard、Mail 和许多其他 OS X 应用使用的 Mac OS X 系统框架版本的名称。Chrome 也是基于 WebKit 的。
目前,只有 Safari 和 Chrome 的最新版本支持这种解决方案。
涉及到什么
允许我们指定使用哪个图像作为遮罩的属性是:-webkit-mask-box-image。
WebKit 的所有特定属性都以-webkit-为前缀。还有 CSS 的 WebKit 属性。目前,这些属性仍然不是 WSC 所描述的 HTML5 标准的一部分,但在不久的将来,它们可能会被添加进来。
-webkit-mask-box-image属性直接在视频标签中指定:
<video src = "comtaste_showreel.mp4" autoplay -webkit-mask-box-image: url(yourMaskImage.png) ;">
url()参数在指定图像的属性中指定。
遮罩图像通常是与视频尺寸相同的 PNG 文件。显示视频的图像区域必须是透明的;要隐藏视频的区域需要是不透明的。
如何建造它
要创建遮罩并将其应用于视频,只需使用特定的标签和遮罩图像。
创建遮罩图像(也称为视频的 alpha 通道)是一项非常简单的任务。可以使用任何图形工具,如 Photoshop、Illustrator、Fireworks 等。有许多在线教程提供了如何执行此操作的分步指导。在这个例子中,我们使用了之前创建的图像,您可以在本章提供的示例的源代码中找到该图像。
一个简单且制作精良的视频教程发布在 Adobe TV 上:[tv.adobe.com/watch/photoshop-for-video/straight-alpha-channels/](http://tv.adobe.com/watch/photoshop-for-video/straight-alpha-channels/)。
以下是完整的解决方案:
`
Solution 5-9: Masking a video 
<video src = "comtaste_showreel.mp4" autoplay style = "position:relative ; top: -325px ; -webkit-mask-box-image: url(maskImage.png) ;" />
`我们已经使用一个图像为视频创建了一个框架(digitalScreenImage.jpg)。为了确保将作为帧的视频元素准确地放置在图像的正确高度,我们通过 CSS 使用了相对定位。事实上,我们在 video 标记中声明了 style 属性,方法是将 video 元素放在距离浏览器上边缘 325 像素的位置,这样它就可以留在图像中。最后,我们应用了带有-webkit-mask-box-image属性的遮罩图像:
<video src = "comtaste_showreel.mp4" autoplay style = "position:relative ; top: -325px ; -webkit-mask-box-image: url(maskImage.png) ;" />
你现在所要做的就是保存文件,并在基于 WebKit 的浏览器中执行它,如谷歌 Chrome 或 Safari,以查看应用于视频的遮罩。
专家提示
除了-webkit-mask-box-image属性,基于 WebKit 的浏览器还有另一个固有属性,允许您将图形效果应用于视频。事实上,可以通过使用-webkit-box-reflect属性来添加视频的反射。
倒影是视频元素的副本,可以出现在视频的左侧、右侧、上方或下方。可以自定义视频和倒影之间的偏移空间。该属性被声明为 style 属性的一个属性,如下面的代码片段所示:
<video src = "comtaste_showreel.mp4" autoplay style = "position:relative ; top: -325px ; "-webkit-box-reflect: below 0px;" / >
这段代码在视频下面添加了一个反射效果。
解决方案 5-9:使用音频元素
音频是一种多媒体元素,也常用于网络。就像视频内容一样,音频在过去也因缺乏开放的网络传输标准而遭受损失。这就是为什么在网络上使用音频内容过去是、现在仍然是委托给第三方插件的原因。
QuickTime、Windows Media Player、Flash Player 和 Real Player 是开发人员用来播放音频内容的最常用插件。最后,Flash Player 再次被宣布为获胜者。随着市场渗透率接近 99%的计算机连接到互联网,Flash 保证任何网站都可以发布与此格式兼容的音频内容。
通过使用 Flash Player,可以播放以下格式的音频:AIFF、WAV 和 MP3。
现在 HTML5 引入了一个<audio>标记,允许你插入音频元素,而不必分发任何第三方插件。
就格式而言,HTML5 规范对音频编解码器或容器格式没有任何限制。
MP3、AAC 或 OGG 音频文件都可以。变化的是各种浏览器对这些格式的支持。为了保证尽可能广泛的兼容性,通过source属性提供相同音频元素的不同格式是一个很好的实践。然后,将由浏览器选择它可以播放的第一个列出的源。
涉及到什么
<audio>标签继承了<video>标签的大部分属性和事件。事实上,要插入一个音频元素,您需要做的就是声明标签并指定src属性:
<audio controls autoplay src="audio.mp3">
可用于该标记的属性有:
- src:包含源代码的本地或远程 URL。
- autoplay:指定文件加载后是否播放。
- loop:指定文件是否应该重复播放。
- controls:指定浏览器是否应显示其默认媒体控件。
- preload:指定音频是否必须由页面预加载。它接受以下值:
none、metadata和auto。 - play():播放音频。
- pause():暂停音频。
- canPlayType():告知给定的 MIME 类型是否可以播放。
- buffered():指定音频缓冲部分的开始和结束时间。
因为并非所有设备和浏览器都支持所有类型的音频,所以最好列出尽可能多的<source>元素:
<audio controls autoplay > <source src="mySong.aac" type="audio/mp4"> <source src="mySong.oga" type="audio/ogg"> <source src="mySong.mp3" type="audio/mp3"> </audio>
指定向浏览器声明 MIME 类型的 type 属性也很重要。
通过这样做,浏览器可以识别它是否能够在不加载文件的情况下播放该格式。
如何建造它
以下是如何使用音频元素的示例:
`
Solution 5-10: Using the Audio element
Your browser does not support the new HTML5 audio element
我们声明了一个音频元素,为此我们使用了浏览器的本地音频控制器,一旦用户加载网页,它就会自动播放(见图 5-14 )。
**图 5-14。**原生音频控制器,由 Opera 浏览器渲染
相同的音频文件以不同的格式编码,以允许所有浏览器播放。为此,我们使用 source 标记来声明不同的格式:
<audio controls autoplay > <source src="your_audio.aac" type="audio/mp4"> <source src="your_audio.oga" type="audio/ogg"> <source src="your_audio.mp3" type="audio/mp3">
浏览器从第一个声明的格式开始识别它能够播放的格式。一旦它知道它可以执行哪个文件,它将停止加载其他格式。
总结
如今,媒体元素在网络上非常流行。这就是为什么全新的 HTML5 音频和视频标签备受关注。虽然在网页中嵌入视频非常容易,但在本章中,您已经了解到作为开发人员您面临两个不同的问题:视频元素使用的编解码器的标准定义和浏览器对视频编解码器的采用。Ogg Theora、H.264 和 VP8/WebM 是目前视频元素支持的唯一格式。此外,每个浏览器只支持其中一种视频编解码器,这使得开发人员的日子不好过。
在本章中,您已经学习了如何在网页上嵌入和播放视频、自定义视频控制器以及以不同格式发布视频,以保证视频可供尽可能多的用户使用。您还学习了如何以全屏幕格式打开视频,以及如何对视频应用遮罩。
在下一章中,你将学习 HTML5 canvas 绘图 API,它为你直接在网页和应用中绘图提供了许多可能性。
六、HTML5 绘图 API
HTML5 的一个很酷的特性是,你可以选择动态渲染 2D 形状和位图图像,因为它现在有自己的本地绘图 API。对于 HTML 来说,这是一个巨大的进步,因为 HTML 从创建之初就一直保持着相当的静态和局限。现在那些日子已经过去了,一个新的可能性领域出现了,来处理在 HTML 页面本身中创建的图形。现在可以构建形状、图形、动画甚至游戏,而无需依赖任何外部插件(如 Flash)。这在开发网页和应用时带来了巨大的价值,但在与移动设备兼容时更是如此。
绘图 API 是新的 HTML5 canvas 元素的一部分,它为 2D 绘图提供了一个 API。最近,通过 HTML5 的 WebGL 支持进行 3D 绘图的可能性似乎即将实现。在撰写本文时,很少有浏览器与 3D 绘图兼容,但在不久的将来,它很可能会成为 HTML5 选项的一部分。
所有支持 HTML5 的主流浏览器都与 canvas 元素兼容,因此也与 2D 绘图 API 兼容。然而,当涉及到 Internet Explorer 时,HTML5 兼容性要到 9.0 版本才能实现。画布元素和 2D API 的浏览器和移动设备兼容性如表 6-1 所示。
注意:Internet Explorer 7 和 Internet Explorer 8 与 HTML canvas 元素不兼容。然而,可以通过使用“ExplorerCanvas”扩展来克服这个问题并在 IE 中提供画布支持。你可以在 Google Code 上下载这个扩展,地址:[excanvas.sourceforge.net/](http://excanvas.sourceforge.net/)。你只需要包含下面的 JavaScript 代码:
*<!--[if IE]><script src="excanvas.js"></script><![endif]-->*
解决方案 6-1:如何使用 canvas 元素的绘图 API 在 HTML5 中绘图
canvas 元素是一个可以添加到 HTML5 页面的矩形区域,它提供了各种各样的图形可能性,因为您可以通过其 2D 绘图 API 控制每个像素。canvas 元素本身没有绘图功能;您将在其中创建的所有内容都将使用 JavaScript 语言以编程方式绘制。
在这个解决方案中,我们将定义一个 canvas 元素并准备好使用它,以便您可以利用它的绘图 API。
涉及到什么
设置能够在 HTML5 中绘图的基础和背景非常简单。你只需要定义一个画布元素,<canvas></canvas>。canvas 标记非常简单,只有三个属性:宽度、高度(以像素为单位)和一个标识您正在哪个画布上绘图的 ID。
<canvas id="canvasID" width="300" height="200"></canvas>
您可以在 HTML5 页面中放置画布,并像处理任何其他标签一样对其应用 CSS。
您的<canvas>最初是空的——一个普通的区域——除非您通过 CSS 给它加上边框或背景色。然而,它不会出现在页面上,直到你在里面画了什么东西。画布只是一个普通的矩形,它将构成一个环境,您可以在其中绘制图形、制作动画等等。
要使用画布并在其中绘图,您需要使用 JavaScript。如果你已经用 ActionScript 3 或 Java 之类的语言编程绘图,你会发现这里有很多相似之处。
一旦创建了画布,您需要做的第一件事就是访问它。您可以像对文档对象模型(DOM)的任何其他元素那样做:
var myCanvas=document.getElementById("canvasID");
然后,您可以访问画布呈现上下文,它提供了对绘图 API 及其方法的访问。为了检索上下文对象,canvas 元素使用 DOM 方法getContext(),该方法只有一个参数:上下文的类型。在撰写本文时,2D 的上下文是唯一可用的。我们预计在未来几年,3D 环境将在最流行的浏览器中可用,但是,现在我们必须使用 2D 版本。
var context=myCanvas.getContext('2d');
现在您已经有了画布并可以访问它的 context 对象,您可以开始使用绘图 API 及其方法在 HTML 页面中创建一些好的艺术品了。
如何建造它
首先用您最喜欢的代码编辑器创建一个新的 HTML5 页面:
-
创建一个空白 HTML5 页面。`
solution 6-1/title> </head> <body> </body> </html>` -
通过将 canvas 标签插入页面来创建画布,并以像素为单位设置其宽度和高度。(如果不设置任何尺寸,在任何浏览器中都会默认为 300×150。)
<canvas id="myCanvas" width="300" height="200"></canvas> -
Give it a one-pixel grey border through CSS, as shown in Figure 6-1.
<canvas id="myCanvas" width="300" height="200" style="border:solid 1px #ccc;"></canvas>**图 6-1。**带有单像素边框的普通画布
在你画之前,画布是一个普通的区域,但是你可以像在任何其他 HTML 元素上一样使用 CSS 属性,它会应用背景颜色、图像、笔触等等。
-
Now you can add fallback content that you might want to show for non-supported browsers. An example of fallback content where canvas is not browser-supported is shown in Figure 6-2.
<canvas id="myCanvas" width="300" height="200" stye="border:solid 1px #ccc;"> Your browser doesn't support the HTML5 canvas element. <img src="fallbackPicture.jpg" alt="fallback picture"/> </canvas>图 6-2 来自非 canvas 兼容浏览器的回退内容
注意:Canvas 没有辅助功能。您可以在某种程度上使用回退内容来掩盖这个问题,但它不会完全做到这一点。
-
用 JavaScript 通过 DOM 访问画布。`
var canvas=document.getElementById('myCanvas');
`
-
Retrieve your canvas context object. `
var canvas=document.getElementById('myCanvas');
var context = canvas.getContext("2d"); `
您还可以优化代码,并在一行中访问画布上下文,如下所示:
var context= document.getElementById('myCanvas').getContext('2d');现在你的画布已经可以在 2D 的背景下绘制了。以下是该示例的完整代码:
`
solution 6-1 Your browser doesn't support support the HTML5 canvas element.
`
专家提示
在同一个页面上可以有几个<canvas>元素,但是每个画布上不能有多个上下文。因此,如果您试图从画布中检索另一个上下文对象,实际上您将重置它。
`
var canvas=document.getElementById('myCanvas'); var context = canvas.getContext("2d"); another_context= canvas.getContext("2d");
`
第一个上下文变量现在将返回 null,您可以通过另一个上下文变量访问画布上下文。
解决方案 6-2:使用路径和坐标
现在您已经设置了画布和绘图上下文,让我们看看如何在其中实际绘图。canvas drawing API 为您提供了使用几乎所有图形程序通用的工具进行绘制的选项:笔画、填充、渐变、线条、图元、弧线或贝塞尔曲线。画布绘制是基于矢量图形的;实际绘图是用矢量完成的。然而,一旦绘制完成,它就变成了位图格式,您可以在像素级别与它进行交互。当要在里面画画时,你会处理路径。
这是一个非常简单的 API,它提供了几种方法来允许你绘制自定义的形状。
涉及到什么
当使用路径绘图时,该过程分为两个主要步骤:
-
定义路径,即路径在画布中的起点、终点及其样式(颜色、线宽、strokestyle、fillstyle 等)。这涉及到使用画布的坐标系。
-
Render it by adding its stroke and/or fill.
我们先来看看 canvas 元素的坐标系,了解如何在其中绘制路径。
画布坐标系
画布是一个 2D 网格,其坐标基于常规的 2D 坐标系:垂直 Y 轴、水平 X 轴和位于 x=0,y=0 的原点。
画布的坐标总是由像素决定的。原点“0,0”位于左上角,像素值随着沿 x 轴和 y 轴的移动而增加。回想一下解决方案 6-1,当你定义一个画布时,你给了它一个高度和一个宽度(如果没有,这个值默认为 300×150 像素)。绘图网格的尺寸由画布标签大小值决定。
每当您想要在上下文中放置一个元素时,您首先必须给出它在画布上的位置。你可以通过传递 x 和 y 值来实现,如图 6-3 中的所示。正的 x 值将向右移动,正的 y 值将向下移动。
图 6-3 一个画布的坐标系和一个点(7,6)的坐标和位置。
绘制路径
在 HTML5 中使用路径绘图包括绘制形状的大致轮廓(可以是任何形状)。当它结束时,您将通过定义它的笔画和/或填充颜色使它出现在上下文中。一旦你的路径完成,你的形状被渲染,如果你想画另一个形状,只要用一个新的路径重新开始。值得注意的是,每个画布只能有一个路径,因此创建一个路径将自动关闭前一个路径(如果有的话)。
创建路径时,可以连接多个子路径并绘制复杂的形状。您可以使用不同的方法来构造每个子路径:lineTo()、quadraticCurveTo()、bezierCurveTo()和arc()。每个连续子路径的终点成为上下文网格上的新起点,这将一直持续到您最终关闭路径。
路径绘制过程始终遵循以下顺序:
- 通过调用
beginPath()方法启动路径。 - 使用
MoveTo(x,y)方法定义您想要在画布网格中开始路径的坐标,并传递您想要开始路径的 x 和 y 坐标。(使用arc,,您将把这些坐标作为方法本身的参数传递,并且您不需要使用MoveTo())。 - 使用 API 的方法来绘制您的形状,并根据您的需要添加尽可能多的子路径。每次添加子路径时,必须使用
MoveTo()方法定义前一个子路径的最后一个点的坐标。 - 使用
closePath ()方法结束路径。这将完成形状。(如果您在上述步骤中使用的方法完成了路径,那么closePath ()方法什么也不做。) - 最后,调用
stroke ()或fill()方法给你的形状添加一个笔画或填充,或者两者都添加。注意,如果你使用的是fill()方法,它会自动关闭路径,然后填充形状,所以在这种情况下没有必要调用closePath()方法。
提示:尽管 canvas 上下文默认为一个新子路径,但是在创建每个新路径之前使用beginPath()方法是一个很好的实践。
2D API 提供了以下方法在画布上绘制路径。同样,记住实际的绘制是在画布上下文中完成的。
-
Drawing a line path. You will define the starting point of your line with the
moveTo()method and pass the coordinate of the ending point of your line by using thelineTo()method (see Figure 6.4).方法 : context.lineTo(x,y);
图 6-4 绘制一条直线路径
-
Drawing a Bezier curve. A Bezier curve is normally defined by a starting point, an end point, and two control points. You set the starting point with the
moveTo()method. You then have to add the coordinates of the two control points and of the ending point as parameters of thebezierCurveTo()method (see Figure 6.5).方法:context . beziercurveto(control point 1x,controlPoint1Y,controlPoint2X,controlPoint2Y,endPointX,endPointY);
图 6-5 绘制一条贝塞尔曲线
-
Drawing a quadratic curve. Here you only have one control point, which will define the required curve by creating two invisible tangents connected to the starting point and the ending point, as shown in Figure 6.6. Thus, the parameters of the
quadraticCurveTo()method will be the coordinates of the control point and the ending point. The starting point is, like the other paths, defined through theMoveTo()method.方法:context . quadratic curveto(control pointx,controlPointY,endingPointX,ending pointy);
图 6-6 绘制二次曲线
-
Drawing an arc. You need to provide first the x and y coordinates of the center of your arc on the canvas context, then the starting angle and ending angle of your arc (they are set in radians), and finally whether you want to draw it counter-clockwise (true) or not (false), as shown in Figure 6-7.
方法:圆弧(中心,圆心,半径,起始角,终止角,逆时针;
图 6-7 画圆弧
为你的形状设置样式
有几个画布上下文属性可让您对形状、线条和笔画应用自定义样式。
线条属性和样式
-
Line join style. If you want to draw a shape with several subpaths, there is a line property that addresses the rendering of the line connections:
lineJoin. Paths can have three possible line joins:miter,round, orbevel, as shown in Figure 6-8. By default, it's set tomiter.图 6-8 线连接
-
Line width style. You can also set the width of your line with the
lineWidthproperty. The size should be given in pixels. By default, it's set to 1, and shown in Figure 6-9.图 6-9 线条宽度样式
-
*线条结束样式:*你也可以通过
lineCap属性在任何画布线条的末端添加一个线帽。线条可以有三种不同的帽样式:butt、round或square,,如图图 6-10 所示。默认情况下,它被设置为butt线帽样式。
你可能会注意到,这两种发型看起来很相似。这是因为butt属性添加的是垂直于线条方向的平边,而square值添加的是一个长度为线条宽度、宽度为线条一半的矩形。
图 6-10 线条末端样式
如何建造它
为了了解如何在画布 API 中使用路径和坐标,我们将向您展示如何绘制 friendsofED 徽标(参见图 6-11 ),因为它需要使用不同种类的路径来呈现您在解决方案 6-1 中已经创建的画布。
图 6-11的朋友们摆出了的标志
-
创建一个空白的 HTML5 页面,并添加一个宽和高为 500 像素、ID 为“
solution 6-2/title> </head> <body onload="drawLogo()"> <canvas id="myCanvas" width="500" height="200">Your browser does not have support for HTML 5 Canvas.</canvas> </body></html>`myCanvas”的画布。您应该为没有支持 HTML5 canvas 元素的浏览器的用户添加一些后备内容。` -
从现在开始,一切都用 JavaScript 来完成。您需要编写一个包含绘图过程代码的
drawLogo()函数。(在这个解决方案中,我们将脚本包含在<head>标签页面中,但是您也可以选择将您的脚本具体化,如下所示:<script scr="yourcode.js"></script>)。您要做的第一件事是创建对画布及其上下文的访问。`var canvas = document.getElementById("myCanvas"); var context = canvas.getContext("2d");`
-
Now that you have your context object, you can start drawing the designed mouse of the logo. When decomposing it, you have half a line crossing it at its center, and opposite to it a closed half circle. `context.lineWidth=4;
context.beginPath(); context.moveTo(190, 100); context.arc(190,125,25,Math.PI1.5,Math.PI.5,true); context.stroke();
context.beginPath(); context.moveTo(165,125); context.lineTo(190,125); context.stroke();
context.beginPath(); context.moveTo(198, 100); context.arc(198,125,25,Math.PI1.5,Math.PI.5,false); context.closePath(); context.stroke();`
在该绘图中,您希望绘制的任何形状都具有相同的线宽。在定义上下文之后,您只需将
lineWidth属性定义为 4 个像素。因此,所有线条都将具有此宽度,您不需要为您绘制的每个形状指定此宽度。如果你想要每个形状有不同的线宽,你只需要为你创建的每个路径定义一个不同的lineWidth。在上面的代码中,您打开了一个新的路径来绘制每个形状。你也可以看到为什么自己关闭路径是重要的。在第一个形状的情况下,您绘制了一个半弧,但您希望笔画只出现在圆周上,而不是半径上。所以,你在定义了你的圆弧周长之后,调用了
stroke()方法。这将封闭路径,并仅在圆周上绘制一个笔划。在第二个弧的情况下,通过闭合连接起点和终点的路径,在它们之间添加子路径。因此,您的笔画也包括起点和终点之间的线。 -
Now draw the mouse wire shape, which is composed of several Bezier curves.
context.beginPath(); context.moveTo(73.3, 121.0); context.bezierCurveTo(73.3, 121.0, 76.7, 123.7, 81.3, 123.3); context.bezierCurveTo(81.3, 123.3, 100.3, 116.5, 104.0, 117.0); context.bezierCurveTo(112.0, 118.0, 120.0, 131.3, 125.3, 131.0); context.bezierCurveTo(129.0, 130.8, 136.7, 127.0, 136.7, 127.0); context.bezierCurveTo(136.7, 127.0, 144.7, 120.3, 148.3, 126.3); context.bezierCurveTo(153.5, 134.8, 158.0, 128.0, 158.0, 128.0); context.stroke(); }</script> </head>这里你又开始了一个新的路径,然后你定义了制作这个多曲线形状所需的子路径,然后你调用了
stroke()方法。现在您已经有了所有的形状,您可以关闭您的函数和脚本。 -
To have your shape rendered when the page loads, add a call to the ‘
drawLogo()' function on the page load in the body tag.<body onload="drawLogo()">这是该示例的完整代码:
`
solution 6-2 .5,true); context.stroke();context.beginPath(); context.moveTo(165,125); context.lineTo(190,125); context.stroke(); context.beginPath(); context.moveTo(198, 100); context.arc(198,125,25,Math.PI1.5,Math.PI.5,false); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(73.3, 121.0); context.bezierCurveTo(73.3, 121.0, 76.7, 123.7, 81.3, 123.3); context.bezierCurveTo(81.3, 123.3, 100.3, 116.5, 104.0, 117.0); context.bezierCurveTo(112.0, 118.0, 120.0, 131.3, 125.3, 131.0); context.bezierCurveTo(129.0, 130.8, 136.7, 127.0, 136.7, 127.0); context.bezierCurveTo(136.7, 127.0, 144.7, 120.3, 148.3, 126.3); context.bezierCurveTo(153.5, 134.8, 158.0, 128.0, 158.0, 128.0); context.stroke(); }
`
专家提示
当你想制作更复杂的形状时,用贝塞尔曲线绘图会变得非常乏味,因为这涉及到你所画内容的计算。避免这种情况的一种方法是用任何矢量图形程序绘制图形,然后以 SVG 格式导出文件。然后,您可以用任何代码编辑器打开它,查看曲线的坐标。然后,您可以在 JavaScript 代码中轻松使用它们。
此外,如果您正在使用 Adobe Illustrator,有一个很棒的插件可以帮您省去所有的辛苦工作。它被称为 AI2Canvas,它允许您将文件直接导出为 HTML5 文件,其中包含 Canvas 元素和相应的 JavaScript 代码。它还会转换颜色和渐变。当然,您可能想要重新编写代码来优化它,但是这是一个与设计人员一起工作并避免繁重计算的很好的插件。你可以在这里下载插件:[visitmix.com/labs/ai2canvas/](http://visitmix.com/labs/ai2canvas/)。
解决方案 6-3:绘制形状:矩形和圆形
在这个解决方案中,您将看到如何使用 HTML5 绘图 API 绘制圆形和矩形。我们已经在解决方案 6-2 中介绍了自定义形状路径的使用,现在您将看到 API 为特定形状提供的方法。
涉及到什么
圆,或者我们应该说是弧,和矩形的特定方法也使用画布路径。这意味着它遵循您在解决方案 6-2 中看到的相同程序:
-
设置上下文画布网格上的坐标
-
设置填充和描边的样式
-
Applying the fill and/or stroke to complete the process and render the drawing.
我们已经在解决方案 6-2 中提到,每个画布一次只有一条路径,所以你只需为每个圆形或矩形创建一条新路径,就像你为每个自定义形状所做的那样。这些将通过 canvas 元素的 context 对象呈现,就像在画布中绘制的任何其他东西一样。先说长方形。
绘制矩形
与 SVG 不同,HTML5 canvas 只知道如何绘制一种基本形状——矩形。所有其他形状必须通过组合一个或多个路径来创建。有四种不同的方法允许你在画布上画一个矩形;你选择的方法取决于你的目标。
您可以使用rect()方法,它接受这些参数:矩形左上角的画布上下文网格上的 x 和 y 位置,以及矩形的期望宽度和高度。语法如下:
rect(x, y, rectangle width, rectangle height);
rect()方法实际上给绘图上下文路径添加了一个矩形路径。您不需要在这里调用beginPath()方法,因为rect()方法本身就可以做到这一点。然后stroke()或fill()方法将在上下文中呈现它。- 您可以通过调用
fillRect()方法直接绘制填充矩形,该方法采用与rect()方法相同的参数。这就像是将rect()和fill()方法合二为一。fillRect(x, y, rectangle width, rectangle height); - 调用
StrokeRect()方法会直接画一个描边矩形。它采用与前两种方法相同的参数。strokeRect(x,y,rectangle width, rectangle height); - 最后一种方法是
clearRect()。首先,此方法不绘制矩形,而是从另一个形状中减去一个矩形。(如果你熟悉 Illustrator,就像使用‘探路者’工具的减法选项一样)。clearRect()采用与rect()方法相同的参数。clearRect(x,y, rectangle width, rectangle height);
画圆
对于圆,您需要使用我们在解决方案 6-2 中使用的圆弧方法,然后绘制一个起始角度为 0,结束角度为 2×π弧度的圆弧,绘制一个完整的圆弧。
画圆弧的方法是arc(),它采用以下参数:
-
画布网格上圆心的 x,y 坐标
-
以弧度指定的开始角度和结束角度值。因为要画一个圆,所以从 0 开始,到 2×pi(相当于 360 度的弧度)结束
-
A Boolean parameter indicating whether you want to draw your arc counter-clockwise (true) or not (false). In the case of a circle, it will work either way of course, so you can set it to either of these
arc(x center coordinate, y center coordinate, 0,Math.pi*2,true)当它创建一个路径时,
arc()方法将只设置你想要画的圆形。要渲染它,你必须调用stroke()或fill()方法,这取决于你的需要。(由于你在这里画了一个完整的圆弧,所以closePath()的方法就没必要了)。
如何建造它
为了演示这个解决方案,您将通过编程绘制一个圆形和两个矩形,如图 6-12 所示。内部矩形将是一个正方形,填充和描边。这是一个非常基本的绘图,但它涵盖了与这两个形状相关的方法。请记住,绘图 API 本身很简单,但这并不意味着你不能用它来绘制复杂的东西——远非如此。这完全取决于你如何通过将几个简单的形状相加来构建复杂的形状。以下代码的结果将如下所示:
图 6-12 绘制复杂形状
-
打开一个基本的空 HTML5 页面,如解决方案 6-1,有一个画布和访问其绘图上下文。`
solution 6-3 ` -
Create a
drawShapes()function that will be called on the load page event. The first thing to do is to access your canvas and its context object.<script type="text/JavaScript" language="JavaScript"> function drawShapes(){ var canvas=document.getElementById("shapes"); var context=canvas.getContext("2d");} context.beginPath(); context.lineWidth="2"; context.strokeRect(10,10,200,280);这里有一个矩形,宽 200 像素,高 280 像素,左上角在画布上的坐标(10,10)处。矩形的笔画宽度为 2 个像素。
-
Draw your circle.
context.beginPath(); context.lineWidth="5"; context.arc(110,150,90,0,Math.PI*2,true); context.stroke();您的圆的中心将位于点 110,150,这是您的矩形的中心(矩形本身位于画布的 10,10)。它的半径为 90 像素,笔画为 5 像素。
-
To finish, draw a filled square at the center of your circle.
context.beginPath(); context.fillRect(80,120,60,60);你开始了一条新的路径,用
fillRect()方法,你画了一个位于圆心的黑色小方块。(除非另有定义,否则填充的形状将始终为黑色。) -
To have your shape rendered when the page loads, add a call to your
drawShapes()function on the page load in the body tag.<body onload="drawShapes()">以下是该示例的完整代码:
`
solution 6-3 `
专家提示
如果您不熟悉 JavaScript 或用其他语言(如 ActionScript 或 Java)以编程方式绘图,一开始可能会有点困惑。Canvas Console 是一个很棒的在线工具,可以帮助你尝试 Canvas 绘图 API 的所有方法并熟悉它们的使用(见图 6-13 )。这个工具是由领先的 HTML5 和 CSS 专家约翰·奥尔索普编写的,它可以让你在上下文中绘制任何东西,并立即看到结果。它还有一个包含所有方法和属性的小备忘单。你可以在这个地址试试:[westciv.com/tools/canvasConsole/](http://westciv.com/tools/canvasConsole/)
图 6-13 帆布控制台
解决方案 6-4:用纯色填充形状
在前两个解决方案中,我们讨论了绘制自定义和基本形状。您只使用了默认的线条和填充样式,默认的填充颜色是黑色。我们现在将探索绘图 API 的选项来填充您的绘图,您将看到如何添加一些纯色。
涉及到什么
首先让我们看看如何简单地用基本颜色填充你的形状。
设置 fillStyle 属性
要填充 HTML5 画布形状,需要使用画布上下文的fillStyle属性。有了它,你可以定义特定的颜色来填充你的形状。然后fill()方法,或者在矩形情况下的fillRect(),将应用这个属性,并使用您定义的样式呈现您的形状。
颜色属性可以通过参考其 RGB 颜色值来定义:
canvasContext.fillStyle = "rgb(255,0,255)";
或其十六进制值:
canvasContext.fillStyle = "0xcccccc";
如果在没有定义任何fillStyle的情况下调用fill()方法,那么将应用默认颜色,黑色。
注意:正如你在解决方案 6-3 和 6-4 中看到的,形状,无论是否自定义,都是由画布上下文路径的子路径组成的。由于每个画布只有一个路径,调用fill()方法会自动关闭路径。这意味着,如果您的子路径组合没有连接自己,该路径将关闭,并将最后一个端点与起始路径点连接起来,并填充该区域。因此,您应该小心您希望这发生在哪里,但它也可以节省您一些绘图代码。例如,要画一个填充的三角形,你只需要画两条线,在渲染的时候,API 会创建最后一条线来封闭路径。
混合颜色
当您用纯色填充形状时,绘图 API 还会将 alpha 混合值添加到可以使用的混合中。代替我们上面讨论的rgb()属性,您必须使用属性rgba(),其中‘a’代表 alpha 值,是最后一个参数。alpha 值从 0(无透明度;即纯色)到 1(100%或完全透明)。这在图 6-14 中进行了说明。
图 6-14 两个矩形重叠,每个矩形的 alpha 值为 0.5
填充笔画
您还可以使用strokeStyle属性,然后调用stroke()方法,为在画布上下文中绘制的形状和文本的笔画设置纯色填充,这将根据您定义的样式(大小、颜色等)呈现形状的笔画。
与fillStyle一样,fillStroke颜色属性可以参照其 RGB 颜色值来定义:
canvasContext.fillStroke="rgb(255,0,255)";
或其十六进制值:
canvasContext.fillStroke="0xcccccc";
注意:如果你为一个形状设置了填充和描边,在stroke()之前调用fill()。否则,填充将覆盖一半的笔画。
如何建造它
在上一个解决方案中,您在画布中绘制了几个形状。你现在将填充一些纯色。
-
打开前面的解决方案代码。您将拥有一个 300 x 300 像素的 HTML5 页面。加载时,您已经在其中绘制了几个形状:矩形内的一个圆。`
` ` solution 6-4/title> </head> <body on load=drawShapes()> <canvas id="myCanvas" width="300" height="300"></canvas> </body> </html>` -
从访问画布的上下文对象开始,添加 JavaScript 代码并编写绘制形状的函数。
<script type="text/JavaScript" language="JavaScript"> function drawShapes(){ var canvas=document.getElementById("myCanvas"); var context=canvas.getContext("2d"); -
Inside the function
drawShapesthat you created, now draw your rectangle by first defining thefillStyleproperty. In this example, use the red color to fill it. You can either use its hexadecimal value (#ED1C24), or rgb (237,28,36).context.fillStyle="#ED1C24"; context.fillRect(0, 0,200,200);对于这一个,你没有使用任何笔画颜色,所以不会出现笔画。这里有一个 200 乘 200 的正方形,它的左上角将被放置在画布的左上角,因为坐标被设置为 0,0。
使用以下代码会得到相同的结果:
context.beginPath(); context.fillStyle="#ED1C24"; context.rect(0, 0,200,200); context.fill(); -
Now you can start a new path and draw your circle.
context.beginPath(); context.lineWidth="4"; context.strokeStyle="#1B1464"; context.fillStyle="#29ABE2"; context.arc(100,100,50,0,Math.PI*2,true); context.stroke(); context.fill(); }在这里,您定义了您的圆的样式属性(笔画宽度、笔画颜色和填充颜色),然后定义了它的位置和大小,最后您通过调用
fill()和stroke()方法像往常一样渲染它。 -
You can now close your script tag.
</script>和前面的解决方案一样,在加载页面时调用
drawShapes()函数来加载画布上下文。(或者,您可以在脚本末尾调用它,结果也是一样的。).结果见图 6-15 。<body onload=drawShapes()>图 6-15 填充素色的形状
这是该示例的完整代码:
`
solution 6-4 `
专家提示
到目前为止,我们一直在处理非常简单的形状组合,除了绘图之外没有任何真正的目的。然而,当您开始构建更复杂的画布并以交互方式使用它们时,您将不得不非常小心代码的性能。绘图可能相当耗费处理器资源,一旦你在画布上画了某样东西,你就不能真正把它从其他被画的对象中分离出来;也就是说,您正在创建单个位图图形。因此,任何改变都意味着重新绘制。因此,在某些情况下,将 CSS 和绘图 API 结合起来是有益的。
如果你已经画了一个背景,比如说,你知道你不想在每次交互时改变,通过 CSS 把它作为画布的背景图像是一个好主意。因此,您的上下文将只重绘必须更改的内容,并为您的应用节省一些渲染开销。
解决方案 6-5:使用渐变填充形状
现在我们知道了如何绘制填充纯色的形状,让我们看看如何用渐变填充它们。
涉及到什么
与任何标准绘图程序一样,API 提供了两种类型的渐变:线性和径向。
使用线性渐变
要用 HTML5 Canvas 创建线性渐变,需要调用createLinearGradient()方法,这需要四个参数通过创建一条不可见的线来定义渐变的方向(参见图 6-16 )。
var myGradient=context.createLinearGradient(lineStartingPointX, lineStartingPointY, lineEndingPointX, lineEndingPointY);
图 6-16 一个线性渐变
一旦你创建了你的渐变方向并设置了它的坐标,你可以使用addColorStop属性插入它的颜色。您可以根据需要向渐变中添加任意数量的颜色,并使用以下方法使用两个参数“偏移”和“颜色值”来创建微妙的渐变填充形状和笔画:
myGradient.addColorStop(offset value, color1); myGradient.addColorStop(offset value, color2);
使用径向渐变
要用 HTML5 画布创建径向渐变,您将使用createRadialGradient()方法。
它有以下六个参数,如表 6-2 所列:
var gradient=context.createRadialGradient(startCircleX, startCircleY, startCirlceRadius, endCircleX, endCircleY, endCirleRadius);
**表 6-2。**createaradialgradient()方法的参数和值
| **参数** | **值** | | :-- | :-- | | startCircleX | 第一个渐变圆的 x 坐标 | | startCircleY | 第一个渐变圆的 y 坐标 | | startcirclrceradius | 第一个渐变圆的半径,以弧度为单位 | | endCircleX | 第二个渐变圆的 x 坐标 | | endCircleY | 第二个渐变圆的 y 坐标 | | endCirleRadius | 第二个渐变圆的半径,以弧度为单位 |这些参数是定义渐变位置和渐变的两个假想圆的坐标和半径。就像线性渐变一样,你可以用addColorStop方法添加任意多的颜色来满足你的需求。
图 6-17 径向梯度
你可以在图 6-17 中看到一个半圆形状的两种颜色的径向渐变的例子,以及渐变参数中定义的两个不可见的圆。渐变从半径为 40 像素的第一个圆开始,向半径为 300 像素的第二个圆径向移动,根据颜色的偏移值(0 和 1)部署颜色。定义该径向梯度的代码如下:
var gradient=context.createRadialGradient(350,310,40,350,310,300); gradient.addColorStop(0,"#6699cc"); //light blue color gradient.addColorStop(1,"#cee7fa"); //darker blue color
用渐变填充笔画
以同样的方式,你可以用渐变填充你的形状的笔画,方法是用渐变定义Fillstyle,然后使用strokeFill()方法。以下示例将使用渐变填充方形笔画。
gradient=context.createLinearGradient(20, 120, 160, 120); gradient.addColorStop(0, "rgb(0, 0, 0)"); // black color gradient.addColorStop(1, "rgb(255, 255, 255)"); // white color context.strokeStyle=gradient; context.rect(10, 10,200,200); context.stroke();
如何建造它
-
和其他解决方案一样,从在 HTML5 页面中设置画布开始。加载后,调用函数
solution 6-5 `drawGradientShapes(),该函数将包含用于绘制两个填充了渐变的形状的 JavaScript。` -
现在来看 JavaScript 部分和
drawGradientShapes()函数。首先,访问您的画布及其上下文对象。然后继续绘制自定义形状。`context.beginPath(); context.moveTo(126.7, 185.6); context.bezierCurveTo(116.5, 192.0, 85.4, 160.2, 85.4, 160.2); context.bezierCurveTo(85.4, 160.2, 50.5, 187.6, 41.2, 179.9);
context.bezierCurveTo(31.9, 172.1, 52.5, 132.8, 52.5, 132.8); context.bezierCurveTo(52.5, 132.8, 15.7, 108.1, 20.2, 96.8); context.bezierCurveTo(24.7, 85.6, 68.4, 93.1, 68.4, 93.1); context.bezierCurveTo(68.4, 93.1, 80.6, 50.4, 92.7, 51.2); context.bezierCurveTo(104.7, 52.0, 111.2, 95.9, 111.2, 95.9); context.bezierCurveTo(111.2, 95.9, 155.5, 94.3, 158.5, 106.0); context.bezierCurveTo(161.5, 117.7, 121.7, 137.4, 121.7, 137.4); context.bezierCurveTo(121.7, 137.4, 136.9, 179.1, 126.7, 185.6); context.closePath();` -
Your shape path is now complete, and you can now define its
fillStyle. Here you'll use a linear gradient with two colors.gradient=context.createLinearGradient(19.8, 118.8, 158.7, 118.8); gradient.addColorStop(0.00, "rgb(122, 226, 28)"); gradient.addColorStop(1.00, "rgb(200, 255, 47)"); context.fillStyle=gradient;你刚刚定义了你的线性渐变,给它你希望你的渐变跟随的那条看不见的线的坐标。然后定义渐变颜色,最后应用到你的上下文
fillStyle。 -
您像往常一样定义
lineWidth和笔画的颜色。context.lineWidth=2; context.strokeStyle = "rgb(127, 127, 127)"; -
调用
stroke()和fill()方法来完成这个过程,并在上下文中呈现您的形状。context.stroke(); context.fill(); -
You will now draw a circle with a radial gradient.
context.beginPath(); context.arc(240,190,50,0,2*Math.PI,true); context.closePath(); gradient=context.createRadialGradient(240,190,10,240,190,50); gradient.addColorStop(0.00, "rgb(100, 181, 251)"); gradient.addColorStop(0.5, "rgb(139, 203, 253)"); gradient.addColorStop(1.00, "rgb(178, 224, 255)"); context.fillStyle = gradient; context.fill();你开始一条新的道路,定义你的圈子,然后是它的风格。这是一个有三种颜色的径向渐变。最后一个渐变圆的坐标在你形状的中心,第二个渐变圆的坐标和半径与你实际的圆相同。它会产生一个三种颜色的渐变,从你的圆的中心开始,向它的边缘辐射。
为了呈现最终结果,您调用了
fill()方法。 -
你现在可以关闭你的函数和脚本,欣赏你的杰作(见图 6-18 )。
} </script>
**图 6-18。**填充渐变的自定形状
这是该示例的完整代码:
`
solution 6-5 gradient=context.createLinearGradient(19.8, 118.8, 158.7, 118.8); gradient.addColorStop(0.00, "rgb(122, 226, 28)");
gradient.addColorStop(1.00, "rgb(200, 255, 47)");
context.fillStyle=gradient;
context.lineWidth=2;
context.strokeStyle = "rgb(127, 127, 127)";
context.fill();
context.stroke();
context.beginPath();
context.arc(240,190,50,0,2*Math.PI,true); context.closePath();
gradient=context.createRadialGradient(240,190,10,240,190,50);
gradient.addColorStop(0.00, "rgb(100, 181, 251)");
gradient.addColorStop(0.5, "rgb(139, 203, 253)");
gradient.addColorStop(1.00, "rgb(178, 224, 255)");
context.fillStyle=gradient;
context.fill();
}
解决方案 6-6:在画布上绘制文本
canvas drawing API 允许您在 canvas 元素中操作和绘制文本。在本解决方案中,我们将向您展示如何做到这一点。
涉及到什么
绘制文本与绘制形状和路径没有太大区别。它遵循类似的过程,除了不是首先描述路径或形状,而是通过属性 font 指定想要使用的字体和字体大小,然后用 stroke 和/或 fill 方法呈现文本。
我们之前看到的所有渲染样式,颜色、渐变等等,可以像应用于任何其他形状一样应用于文本。CSS 样式也可以用于通过画布 id 绘制的文本。
管理画布中的文本不像使用 CSS 管理常规文本那样可定制。但是,让我们看看您可以使用的几个属性。
字体属性
在画布中绘制的文本继承了<canvas>元素本身的字体大小和样式(如果有的话),但是您可以通过在绘图上下文中设置 font 属性来覆盖它们。
您可以在那里定义的字体属性与您在 CSS 中设置的属性相同——字体样式、字体粗细、字体大小和字体系列。语法如下:
context.font="bold 14px Arial" ;
默认字体是 10 像素的无衬线字体。
对齐文本属性
您可以设置textAlign属性来对齐画布上下文中的文本。对齐是相对于您为文本建立的 x 和 y 位置设置的。它可以有以下值:left、right、start(默认值)、 end 和 center。开始和结束属性基于画布标记的文本方向,从左到右或从右到左。
文本基线属性
无需深入解释,假设文本对齐基于包含文本的行框,并且每个行框都有基线。(通过打开下面的 HTML5 规范链接,您可以看到表示这一点的图形。)基线决定了文本在行框中的位置,其值可以是:顶部、悬挂、中间、字母(默认)、表意和底部。
对于基于拉丁文的字母,最有可能只使用顶部、底部、中间和字母。如果您必须处理其他字母,您可以参考 HTML5 规范网页上的完整基线描述:
www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d -textbaseline
理解画布上的文本呈现被视为与任何其他路径相同的方式是很重要的。因此,可以描边和填充文本,并且所有呈现样式都可以应用于文本,就像它们应用于任何其他形状一样。也就是说,正如您可能期望的那样,文本绘制例程遵循与任何路径相同的过程:
-
定义您的文本样式。或者,与其他形状一样,您可以指定
fillStyle、strokeStyle、lineStyle和lineWidth属性,它们的应用方式与其他形状相同。 -
Call the
strokeText(), orfillText()method to render the text on the canvas.这两个方法都有三个参数:画布上文本的 x 和 y 坐标,以及要绘制的字符串。正如您所猜测的,
strokeText()将只绘制字体的笔画,fillText()将绘制填充的文本。context.fillText(x,y,"string"); context.strokeText(x,y,"string");
注意:你可能知道,CSS3 现在提供了@font-face属性,允许你从远程位置加载特定的字体。通过 CSS 设置画布字体属性,您可能会尝试将它用于您的画布文本。然而,当您调用fillText()或strokeFill()方法时,画布上的绘制必须立即发生,浏览器通常不会在上下文呈现时从其位置加载字体。所以最有可能的是,它将会回到默认字体。
每当你想在画布上使用文字时,有几件事必须要考虑。首先,画布中呈现的任何内容都是不可访问的,文本也是如此。实际上,在其中绘制的文本也是不可选择的。您可能会尝试使用 canvas fallback 内容来克服这个问题,但它并不真正适合这种需求。
如何建造它
-
创建一个带有 canvas 元素的基本 HTML5 页面。`
solution 6-6/title> <script> </script> </head> <body> <canvas width="500" height="300" id="myCanvas" style="border:1px solid #ccc"></canvas>` -
在页面的顶部,添加一个 JavaScript 标签并编写一个
drawText()函数,该函数将包含用于在画布中呈现文本的代码。与前面的解决方案一样,首先要访问画布及其渲染上下文。`var canvas=document.getElementById("myCanvas"); var context=canvas.getContext("2d");
}
`
-
You can start to draw your text inside your function. First, define your text styles.
context.strokeStyle = "#993300"; context.fillStyle=" #ffff09"; context.font="bold 80px arial"; context.textAlign="center"; context.lineWidth="5"; context.lineJoin="round";您已经设置了字体、字体大小、文本笔划和填充颜色。这里您将基线保留为默认值。
-
Call the methods to draw the actual stroked and filled text and close your function.
context.strokeText("TML5",220,90); context.fillText("HTML5",220,90); }在绘制时,必须同时绘制描边和文本填充,才能获得描边文本。
-
在页面的 body 标签中加载页面时调用函数。
<body onLoad="drawText()"> -
结果将如图 6-19 所示:
图 6-19 。背景填充文本
这是该示例的完整代码:
`
solution 6-6 `专家提示
您可能会发现 canvas 元素中的文本可能性有点有限。优秀的 JavaScript 开发人员 Jim Studt 创建了一个有趣的 JavaScript 插件,名为“canvas text”,它扩展了 canvas 上下文并添加了一些额外的方法来处理文本。它将以下方法添加到画布中:
context.drawText / context.drawTextRight / context.drawTextCenter:这将在该位置(基线)以指定的字体和大小绘制文本。context.measureText:返回文本的宽度。context.fontAscent / ctx.fontDescent:返回从基线的上升/下降。
拥有这些额外的方法增加了操纵文本大小和位置的可能性。例如,它们在应用的动态渲染中非常有用。您可以了解关于这些方法的更多信息,并阅读文档,网址:[jim.studt.net/canvastext/](http://jim.studt.net/canvastext/)
解决方案 6-7:使用相对字体大小在画布上绘制文本
每当你添加文本,你必须设置它的大小。它可以以像素为单位固定,但也可以像处理 CSS 一样相对地设置它。
涉及到什么
就像页面上的其他元素一样,<canvas>有一个基于你在 CSS 中定义的字体大小。这和你在处理你的 HTML4 页面时所习惯的没有什么不同。让我们简单回顾一下相对字体大小的概念,以理解它在 canvas 中是如何工作的。
字体大小、em 和相关文本内容
有两种方法可以设置 HTML 页面中的字体大小,一种是以像素(px)或磅(pt)为单位的绝对值,另一种是相对值,即“em”值..
Em 为你提供了相对于父元素设置字体大小的可能性。在 CSS 中,这对于根据呈现媒体调整样式非常有用。
您可以为画布文本使用 em 值。这是什么意思?给字体指定一个 em 大小就是根据其父元素的字体大小按比例指定其大小。因此,如果您的画布字体设置为 10 px,然后您绘制一个大小为 2 em 的文本,该文本将是画布上没有定义特定字体大小的任何其他文本的两倍大。
context.font="2em Arial"; context.fillText("Arial with a 2em size", 100, 50);
现在,如果你想完全摆脱绝对值,你将不得不设置父元素字体大小为%(你的画布),你将有一个完整的文本相对渲染。如今,尤其是随着智能手机和平板电脑的出现,如果你希望你的网络内容可以在任何平台上使用,照顾不同的配置是至关重要的。
如何建造它
-
Create a basic HTML5 page, and add a canvas element to it. `
` ` solution 6-7/title> </head> <body onLoad="drawText()"> <canvas id="textCanvas" width="400" height="300" style="font-size:100%"></canvas> </body> </html>` 添加 canvas 元素时,将 CSS 字体大小设置为 100%。因此,您的画布字体大小将根据父画布调整字体大小。 -
现在,您可以添加 JavaScript 代码来绘制文本。在这里,您将编写一个
drawText()函数,它将在页面加载时被调用,以便在浏览器中加载页面时立即呈现您的文本。正如在前面的解决方案中看到的,您将首先通过 DOM 访问您的画布,然后通过它的上下文对象进行绘制。然后你将开始定义和绘制你的文本。`function drawText(){ var canvas=document.getElementById('textCanvas');
var context=canvas.getContext('2d');`
-
现在你想写一些文本,大小是标准画布文本的 0.8 倍。为此,您需要重新定义上下文字体属性来覆盖您刚才使用的字体属性,将大小设置为 0.8em,然后使用
fillText()方法绘制新的字符串。context.font="0.8em Arial"; context.fillText("Canvas Text Arial size 0.8em", 10, 50); -
如果您想写两倍于画布中定义的高度的文本,您可以根据需要覆盖 context.font 属性:
context.font="2em Arial"; context.fillText("Canvas Text Arial size 2em", 10, 100); -
To use pixel sizes in the same context, you would do the following:. `context.font="14px Arial"; context.fillText("Canvas Text Arial size 14 pixels", 10, 140);
}
`
你现在可以关闭你的功能。加载页面时,您应该会看到以下视觉效果:
图 6-20 用相对字体大小绘制的文本
这是该示例的完整代码:
`
solution 6-7 `专家提示
如果你想让你的字体大小调整到客户端浏览器的默认值,就像你在 HTML4 中做的那样;也就是说,在 CSS 文件中将页面的默认字体大小设置为 100%。画布作为一个子元素,现在将基于这个值设置它的字体大小。
解决方案 6-8:将形状保存为 PNG 文件
假设您使用 canvas 和 drawing API 在 HTML5 中制作了一个小应用,用户可以在其中生成一些自定义结果(例如,通过绘图应用生成自定义图形或设计),并且您希望他或她能够保存它。canvas 对象提供了一个方法,可以让您将 canvas 上下文转换成 PNG 格式的文件。(也有其他图像格式,但是 PNG 是唯一一种支持 HTML5 规范的格式,所以我们将重点讨论它。)
涉及到什么
要从画布中检索图像数据,您将使用 canvas 对象的toDataURL()方法。
调用时,该方法返回一个 URL,其中包含以 PNG 文件形式表示的画布内容。
var canvas=document.getElementById('myCanvas'); var imageDate=canvas.toDataURL();
检索到的dataUrl的内容将以data:image/png;base64的格式返回,并且是您的 PNG 图像数据的 base64 字符串。如果画布没有像素(即。它的高度和宽度都是空的),那么这个方法只返回字符串data。然后,您可以检索并保存它。
如何建造它
-
用画布创建一个 HTLM5 页面,就像你在本章中对其他解决方案所做的那样。`
solution 8-1/title> </head> <body onload="init()"> <canvas id="canvas" width="400" height="400"></canvas> </body> </html>` -
Add a button in the body of your page with the call to the function
saveCanvason its click event.<button onclick="saveCanvas();">Save PNG</button>HTML5 本身也就这么多了。对于这个解决方案,从现在开始您将只处理 JavaScript。
因为您希望您的画布和上下文可用于您将要编码的几个函数,所以从定义它们各自的变量开始。然后,在一个单独的函数中检索画布和上下文对象,加载页面时将调用该函数。最后,您将调用一个函数(您将在此之后编写该函数)来处理绘制一个简单的形状,并且您将调用
drawShape()。<script type="text/Javascript" language="Javascript"> var canvas; var context; function init(){ canvas=document.getElementById('canvas'); context=canvas.getContext('2d'); drawShape(); } -
对于您的函数
drawShape(),让它变得非常简单,只需在画布上画一个 200 像素宽的红色方块。function drawShape(){ context.fillStyle="red"; context.fillRect(10,10,200,200); } -
Use the function called on your button's click event,
saveCanvas(),where you will retrieve the PNG image data of your canvas. `function saveCanvas(){ urlData=canvas.toDataURL(); window.location=urlData;}`
urlData变量现在将包含 PNG 图像数据的 base64 字符串。 -
Call your
init()function upon the loading of your page.<body onload="init()">当你启动你的页面时,你会看到红色方块和你的按钮,“保存 PNG”如果你点击按钮,它将打开一个新的网页,其网址是你的画布作为一个 PNG 文件,因此你的 PNG 在浏览器中。你可能会反对说这并没有真正挽救你的形象,这是非常正确的。然而,如果你想只使用 JavaScript,如果你需要一个可靠的解决方案,这是可以做到的,因为 JavaScript 是一种客户端语言。在下面的专家提示部分,您将看到如何通过 PHP 和 HTML5 来实现这一点。
专家提示
如果将 Save PNG 按钮与 AJAX 和 PHP 结合使用,就可以从画布中检索 PNG 并强制下载,这样客户机就可以保存 PNG 文件。您可以采用与上面相同的代码,但是当涉及到saveImage()函数时,您将进行不同的处理。
正如您所看到的,通过调用dataToURL方法,您检索了编码为 base64 字符串的 PNG 格式的画布。您可以通过POST方法发送这个变量来做几件事情(GET在大多数情况下不起作用,因为它受到字符大小的限制,图像可以产生很长的字符串):您可以将它保存在服务器端,并允许用户下载它。首先让我们看看如何通过 AJAX 用一个POST变量发送你的图像。为此,您将重写您的saveCanvas()函数。
function saveCanvas(){ var dataUrl=canvas.toDataURL(); var xtr=new XMLHttpRequest(); xtr.open("POST",'saveImage.php',true); xtr.setRequestHeader('Content-Type','application/upload'); xtr.onreadystatechange=function(){ if(xtr.readyState==4 && xhr.responseText=="ok"){ window.location="download.php"; } } xtr.send(dataUrl); }
如果您不熟悉 AJAX,让我们快速浏览一下这段代码。您在这里所做的是将您的 PNG DataURL作为一个POST变量发送到“saveImage.php”中的 PHP 脚本。为了发送画布上下文dataURL,您将使用 AJAX XMLHttpRequest对象。
当您的 PHP 脚本接收到这个变量时,它将解码 base64 编码的文件,创建一个 PNG 文件,将其命名为“canvas.png”,并将其保存在服务器上。如果一切顺利,它将发回字符串“ok”(注意,在这个例子中,您不会处理所有的错误消息。)
XMLHttpRequest对象有一个名为readyState的属性。这是存储服务器响应状态的地方。有了它,你可以通过onreadystatechange属性监听服务器的响应。如果readyState改变了,意味着我们有了服务器响应,并且它等于 4(这意味着来自服务器的响应是完整的),那么您可以检查 PHP 脚本发回的responseText值。如果是字符串“ok”(由您的 PHP 脚本发送的字符串,通知您的 PNG 已被正确创建),您可以进入下一个也是最后一个步骤:通过“download.php”中的另一个小 PHP 脚本强制下载 PNG。
现在让我们看看 PHP 脚本。首先,让我们看一下saveImage.php页面,它在服务器端保存画布图像:。
`<?php if(isset(GLOBALS["HTTP_RAW_POST_DATA"])){ imageData=filteredData=substr(imageData,",")+1); filteredData); fp,decodedData)){ fclose(fp); echo 'ok'; }
} ?>`
这个脚本通过 PHP 函数base64_decode(,)对 base64 数据进行解码,然后将解码后的数据保存到服务器上名为“canvas.png”的 PNG 文件中。如果文件创建和保存正确,它将返回字符串“ok”。
用download.php页面调用的 PHP 脚本将强制下载保存的canvas.png文件,如下所示:
<?php $fsize=filesize('canvas.png'); header('Content-Disposition:attachment;filename="canvas.png"'); header('Content-type:application/force-download'); header('Content-type:image/png'); header('Content-length:'.$fsize.''); readfile('canvas.png'); ?>
现在,当用户单击 Save PNG 按钮时,它将首先在画布中创建一个 PNG,将其命名为“canvas.png”,如果成功,它将打开一个保存对话框窗口,允许用户将其保存到自己的系统中。
这是该示例的完整代码:
HTML5 页面:
`
solution 6-8-2 Save as PNG `saveImage.php 电码:
<?php if(isset($GLOBALS["HTTP_RAW_POST_DATA"])){ $imageData=$GLOBALS['HTTP_RAW_POST_DATA']; $filteredData=substr($imageData,strpos($imageData,",")+1); $unencodedData=base64_decode($filteredData); $fp=fopen('canvas.png','wb'); if(fwrite($fp,$unencodedData)){ fclose($fp); echo 'ok'; } } ?>
download.php 电码:
<?php $fsize=filesize('canvas.png'); header('Content-Disposition:attachment;filename="canvas.png"'); header('Content-type:application/force-download'); header('Content-type:image/png'); header('Content-length:'.$fsize.''); readfile('canvas.png'); ?>
总结
如您所见,HTML5 canvas drawing API 提供了广泛的可能性,允许您直接在网页和应用中进行绘制。您可以使用路径绘制基本或复杂的形状,使用颜色和渐变,以及绘制文本。结合所有这些功能,您无需使用任何第三方插件就可以创建出色的 web 内容。
在下一章,你将看到如何进一步使用 HTML5 画布绘制 API。您将能够操纵画布上下文中的元素来创建强大的 web 内容,并直接使用 HTML5 为其添加交互性和动画。