第15章 使用Canvas绘图
不用说,HTML5添加的最受欢迎的功能就是<canvas>元素。这个元素负责在页面中设定一个区域,然后就可以通过JavaScript动态地在这个区域中绘制图形。
除了具备基本绘图能力的2D上下文,<canvas>还建议了一个名为WebGL的3D上下文。
基本用法:
<canvas id=“drawing” width=“200” height=“200”>a drawing of something.</canvas>
如果不添加任何样式或者不绘制任何图形,在页面中是看不到该元素的。
要在这块画布上绘图,需要取得绘图上下文。
Var drawing = document.getElementById(“drawing”);
if(drawing.getContext){ var context = drawing.getContext(“2d”);}
使用toDataURL()方法,可以导出在<canvas>元素上绘制的图像。
2D上下文:
使用2D绘图上下文提供的方法,可以绘制简单的2D图形,比如矩形、弧线和路径。
(1)填充和描边
2D上下文的两种基本绘图操作是填充和描边。
操作的结果取决于两个属性:fillStyle和strokeStyle,这两个属性的值可以是字符串、渐变对象或模式对象,而且它们的默认值都是“#000000“。
(2)绘制矩形
矩形是唯一一种可以直接在2D上下文中绘制的形状。与矩形有关的方法包括fillRect()、strokeRect()和clearRect()。都接收4个参数:矩形的x坐标、y坐标、宽度、高度。
(3)绘制路径
2D绘制上下文支持很多在画布上绘制路径的方法。通过路径可以创造出复杂的形状和线条。要绘制路径首先必须调用beginPath()方法,表示要开始绘制新路径。然后用下列方法来实际绘制路径:
Arc():绘制弧线;
arcTo():从上一点开始绘制一条弧线;
bezierCurveTo():从上一点开始绘制一条曲线;
lineTo(x,y):从上一点开始绘制一条直线,到(x,y)为止;
moveTo(x,y):将绘图游标移动到(x,y),不画线;
quadraticCurveTo:从上一点开始绘制一条二次曲线;
Rect(x,y):从点(x,y)开始绘制一个矩形;
(4)绘制文本
绘制文本主要有两个方法:fillText()和strokeText()。这两个方法都可以接收4个参数:要绘制的文本字符串、x坐标、y坐标、文本的最大像素宽度(可选)。
(5)变换
通过上下文的变换,可以把处理后的图像绘制到画布上。可以通过如下方法来修改变换矩阵:
Rotate(angle):围绕原点旋转图像angle弧度;
scale(scaleX,scaleY):缩放图像;
translate(x,y):将坐标原点移动到(x,y);
transform:直接修改变换矩阵;
setTransform:将变换矩阵重置为默认状态后,再调用transform。
可以调用save()方法,把当前的上下文设置保存进一个栈结构中,使用restore()方法进行出栈。
(6)绘制图像
2D绘图上下文内置了对图像的支持,如果你想把一副图像绘制到画布上,可以使用drawImage()方法。
最简单的调用方式是传入一个HTML <img>元素,以及绘制该图像的起点的x和y坐标。
除了给drawImage()方法传入HTML<img>元素外,还可以传入另一个<canvas>元素作为其第一个参数。
(7)阴影
2D上下文会根据以下几个属性的值,自动为形状或路径绘制出阴影。
shadowColor、shadowOffsetX、shadowOffsetY、shadowBlur;
(8)渐变
渐变由CanvasGradient实例表示,要创建一个新的线性渐变,可以调用createLinearGradient()方法。创建的gradient对象可以设置到fillStyle或strokeStyle上。
要创建径向渐变(或放射渐变),可以使用createRadialGradient()方法。
(9)模式
模式其实就是重复的图像,可以用来填充或描边图形。可以调用createPattern()方法并传入两个参数:一个HTML<img>元素和一个表示如何重复的字符串(和background-repeat属性值相同)。
将模式对象设置到fillStyle上。
createPattern()方法的第一个参数也可以是一个<video>元素,或另一个<canvas>元素。
(10)使用图像数据
可以通过getImageData()取得原始图像数据。
(11)合成
globalAlpha属性:是一个介于0-1之间的值,用于指定所有绘制的透明度。
globalCompositionOperation:表示后绘制的图形怎样与先绘制的图形结合。
WebGL:
WebGL是针对Canvas的3D上下文。浏览器中使用的WebGL就是基于OpenGL ES 2.0制定的。
(1)类型化数组
类型化数组也是数组,只不过其元素被设置为特定类型的值。类型化数组的核心就是一个名为ArrayBuffer的类型,每个ArrayBuffer对象表示的只是内存中指定的字节数,但不会指定这些字节用于保存什么类型的数据。
Var buffer = new ArrayBuffer(20);
1.视图
使用ArrayBuffer(数组缓冲器类型)的一种特别的方式就是用它来创建数组缓冲器视图。最常见的视图是DataView,通过它可以选择ArrayBuffer中一小段字节。
虽然DataView能让我们在字节级别上读写数组缓冲器中的数据,但我们必须要记住要将数据保存到哪里,需要占用多少字节。这样就有很多工作量,因此类型化视图就应运而生。
2.类型化视图
类型化视图一般也被称为类型化数组,因为它们除了元素必须是某种特定的数据类型外,与常规的数组无异。
(2)WebGL上下文
drawing.getContext(“experimental-webgl”);取得了WebGL上下文之后,就可以开始3D绘图了。
1.常量
在WebGL中,保存在上下文对象中的这些常量都没有GL前缀。
2.方法命名
方法名的后缀会包含参数个数(1到4)和接收的数据类型(f表示浮点数,i表示整数);也可以接收数组,包含字母v。
3.准备绘图
绘图前,调用clearColor()方法来指定要使用的颜色值。
4.视口与坐标
要改变视口的大小,可以调用viewport()方法。
5.缓冲区
顶点信息保存在JavaScript的类型化数组中,使用之前必须转换到WebGL的缓冲区。要创建缓冲区,可以调用gl.createBuffer(),然后使用gl.bindBuffer()绑定到WebGL上下文。这两步做完之后,就可以用数据来填充缓冲区了。
不想要某个缓冲区了,可以调动gl.deleteBuffer(buffer);
6.错误
手工调用gl.getError()方法,输出错误常量。
7.着色器
WebGL中有两种着色器:顶点着色器和片段(或像素)着色器。
8.编写着色器
9.编写着色器程序
为便于使用,通常是把着色器包含在页面的<script>标签内,并为该标签指定一个自定义的type属性。由于无法识别type属性值,浏览器不会解析<script>标签中的内容。
复杂一些的WebGL应用可能会通过Ajax动态加载着色器。
取得了GLSL字符串之后,接下来就是创建着色器对象。可以调用gl.createShader()方法。
10.为着色器传入值
11.调试着色器和程序
调用gl.getShaderParameter()和gl.getShaderInfoLog()方法。
12.绘图
WebGL只能绘制三种形状:点、线和三角。其他所有形状都是由这三种基本形状合成之后,再绘制到三维空间中的。
13.纹理
WebGL的纹理可以使用DOM中的图像。要创建一个新纹理,可以调用gl.createTexture(),然后再将一幅图像绑定到该纹理。
图像、加载到<video>元素中的视频,甚至其他<canvas>元素都可以用作纹理。
14.读取像素
与2D上下文类似,通过WebGL上下文也能读取像素值。读取像素值的方法readPixels()与OpenGL中的同名方法只有一点不同,即最后一个参数必须是类型化数组。
(3)支持
某个浏览器的某个版本实现了它,并不一定意味着就真能使用它,某个浏览器支持WebGL,至少意味着两件事:首先,浏览器本身必须实现了WebgL API;其次,计算机必须升级显示驱动程序。
第16章 HTML5脚本编程
HTML5规范定义了很多新HTML标记,为了配合这些标记的变化,HTML5规范也用了显著篇幅定义了很多JavaScript API。
跨文档消息传递:
跨文档消息传递(Cross Document Messaging),有时简称为XDM,指的是来自不同域的页面间传递消息。
XDM的核心是postMessage()方法,目的是:向另一个地方传递数据。对XDM而言,“另一个地方”指的是包含在当前页面中的<iframe>元素,或者由当前页面弹出的窗口。
接收两个参数:一条消息和一个表示消息接收方来自哪个域的字符串。
接收到XDM消息时,会触发window对象的message事件。传递给onmessage处理程序的事件对象包含以下三个方面的重要信息:
data:传入的数据;
origin:发送消息的文档所在的域;
Source:发送消息的文档的window对象的代理。
XDM已经作为一个规范独立出来,现在它叫做Web Messaging。
原生拖放:
最早在网页中引入JavaScript拖放功能的是IE4。
(1)拖放事件
有些事件是在被拖动的元素上触发的,而有些事件是在放置目标上触发的。
拖动某元素时,将依次触发下列事件:dragstart、drag、dragend。这三个事件的目标都是被拖动的元素。
当某个元素被拖动到一个有效的放置目标上时,下列事件会依次发生:dragenter、dragover、dragleave或drop。这三个事件的目标是作为放置目标的元素。
(2)自定义放置目标
如果拖动元素经过不允许放置的元素,无论用户如何操作,都不会发生drop事件。不过,你可以把任何元素变成有效的放置目标,方法是重写dragenter和dragover事件的默认行为。
(3)dataTransfer对象
dataTransfer对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据。
(4)dropEffect与effectAllowed
dataTransfer对象还有两个属性:dropEffect和effectAllowed。
dropEffect属性可以知道被拖动的元素能够执行哪种放置行为:none、move、copy、link;要使用该属性,必须在ondragenter事件处理程序中针对放置目标来设置它。
dragEffect属性只有搭配effectAllowed属性才有用。
effectAllowed属性表示允许拖动元素的哪种dropEffect,必须在ondragstart事件处理程序中设置。
(5)可拖动
默认情况下,图像、链接和文本是可以拖动的。
HTML5为所有HTML元素规定了一个draggable属性,表示元素是否可以拖动。
(6)其他成员
HTML5规范规定dataTransfer对象还应该包含下列方法和属性:addElement(element)、clearData(format)、setDragImage(element, x,y)、types。
媒体元素:
HTML5新增了两个与媒体相关的标签:<audio>和<video>。这两个标签除了让开发人员方便地嵌入媒体文件之外,都提供了用于实现常用功能的Javascript API,允许为媒体创建自定义的控件。
使用这两个元素时,至少要在标签中包含src属性,还可以设置width和height属性,而为poster属性指定图像的URI可以在加载视频内容期间显示一幅图像。另外,如果标签中有controls属性,则意味着浏览器应该显示UI控件,以便用户直接操作媒体。
(1)属性
如autoplay、controls、currentTime、duration、ended、loop、paused、volume等。
(2)事件
这些事件监控着不同的属性的变化,这些变化可能是媒体播放的结果,也可能是用户操作播放器的结果。
abort:下载中断;
emptied:网络连接关闭;
ended:媒体已播放到末尾;
pause:播放已暂停;
Play:媒体已接收到指令开始播放;
Progress:正在下载;
Timeupdate:currentTime被以不合理或意外的方式更新;
Volumechange:volume属性值或muted属性值已改变;
Waiting:播放暂停,等待下载更多数据;
…
(3)自定义媒体播放器
使用<audio>和<video>元素的play()和pause()方法,可以手工控制媒体文件的播放。组合使用属性、事件和这两个方法,很容易创建一个自定义的媒体播放器。
(4)检测编解码器的支持情况
这两个媒体元素都有一个canPlayType()方法,该方法接收一种格式/编解码器字符串,返回“probably“、“maybe”或“”。
(5)Audio类型
<audio>元素还有一个原生的JavaScript构造函数Audio,可以在任何时候播放音频。从同为DOM元素的角度看,Audio与Image很相似,但Audio不用像Image那样必须插入到文档中。
历史状态管理:
通过hashchange事件,可以知道URL的参数什么时候发生了变化,即什么时候该有所反应。而通过状态管理API,能够在不加载新页面的情况下改变浏览器的URL。为此,需要使用history.pushState()方法,这个方法接收3个参数:状态对象、新状态标题、可选的相对URL。
执行该方法后,新的状态信息就会被加入历史状态栈,而浏览器地址栏也会变成新的相对URL。
按下浏览器“后退”按钮,会触发window对象的popstate事件。该事件对象有一个state属性,这个属性包含着当初以第一个参数传递给pushState()的状态对象。
要更新当前状态,可以调用replaceState(),传入的参数与pushState()的前两个参数相同。
第17章 错误处理与调试
浏览器报告的错误:
1.IE
IE是唯一一个在浏览器的界面窗体中显示JavaScript错误信息的浏览器。在发生JavaScript错误时,浏览器左下角会出现一个黄色的图标。
2.Firefox
3.Safari
4.Opera
5.Chrome
错误处理:
(1)try-catch语句
发生错误时,catch块会接收到一个包含错误信息的对象。与其他语言不同的是,即使你不想使用这个错误对象,也要给它起个名字。这个对象中包含的实际信息会因浏览器而异,但共同的是有一个保存着错误消息的message属性。ECMA-262还规定了一个保存错误类型的name属性。
1.finally子句
如果提供了finally子句,则catch子句就成了可选的。finally子句中的return会覆盖try或catch语句中的return语句。
2.错误类型
ECMA-262定义了7种错误类型:
Error:基类型,其他错误类型都继承自该类型;
EvalError:在使用eval()函数而发生异常时被抛出;
RangeError:在数值超出相应范围时触发;
ReferenceError:在找不到对象的情况下,访问不存在变量时,就会发生这种错误;
SyntaxError:当我们把语法错误的JavaScript字符串传入eval()函数时,就会导致此类错误;
TypeError:在变量中保存着意外的类型时,或者访问不存在的方法时,都会导致这种错误;
URIError:在使用encodeURI()或decodeURI(),而URI格式不正确时,就会导致URIError错误。
3.合理使用try-catch
比如在使用一个大型JavaScript库中的函数,该函数可能会有意无意地抛出一些错误。由于我们不能修改这个库的源代码,所以最好处理一下。
(2)抛出错误
与try-catch语句相配的还有一个throw操作符,用于随时抛出自定义错误。抛出错误时,必须要给throw操作符指定一个值,这个值是什么类型,没有要求。
在遇到throw操作符时,代码会立即停止执行。仅当有try-catch语句捕获到被抛出的值时,代码才会继续执行。
1.抛出错误的时机
Throw new Error(“process():Argument must be an array.”);
2.抛出错误与使用try-catch
(3)错误(error)事件
任何没有通过try-catch处理的错误都会触发window对象的error事件。在任何Web浏览器中,onerror事件处理程序都不会创建event对象,但它可以接收三个参数:错误消息、错误所在的URL和行号。
Window.onerror = function(message, url, line){}
在上述事件处理程序中返回false,可以阻止浏览器报告错误的默认行为。
图像也支持error事件。只要图像的src特性中的URL不能反悔可以被识别的图像格式,就会触发error事件。
(4)处理错误的策略
(5)常见的错误类型
1.类型转换错误
2.数据类型错误
3.通信错误
第一种通信错误与格式不正确的URL或发送的数据有关。最常见的问题是在将数据发送给服务器之前,没有使用encodeURIComponent()对数据进行编码。
(6)区分致命错误和非致命错误
(7)把错误记录到服务器
使用Image对象来发送错误日志请求,这样做有几点好处:所有浏览器都支持Image对象,包括那些不支持XMLHttpRequest对象的浏览器;可以避免跨域限制;在记录错误的过程中出问题的概率较低,大多数ajax库可能会出问题。
调试技术:
(1)将消息记录到控制台
(2)将消息记录到当前页面
(3)抛出错误
常见的IE错误:
(1)操作终止
在IE8之前的版本中,存在一个难于调试的错误:操作终止。
例如:Javascript代码在页面尚未加载完毕时就要修改document.body。
准确一点说,当<script>节点被包含在某个元素中,而且JavaScript代码又要使用appendChild()、innerHTML或其他DOM方法修改该元素的父元素或祖先元素时,将会发生操作终止错误(因为只能修改已经加载完毕的元素)。
(2)无效字符
(3)未找到成员
IE中的所有DOM对象都是COM对象,而非原生JavaScript对象的形式实现的。
具体来说,如果在对象被销毁之后,又给对象赋值,就会导致未找到成员错误。而导致这个错误的,一定是COM对象。
(4)未知运行时错误
当使用innerHTML或outerHTML以下列方式指定HTML时,就会发生未知运行时错误:一是把块级元素插入到行内元素时,二是访问表格任意部分的任意属性时。
(5)语法错误
(6)系统无法找到指定资源
在使用JavaScript请求某个资源URL,而该URL的长度超过了IE对URL最长不能超过2083个字符的限制时,就会发生这个错误。IE对URL路径还有一个不能超过2048个字符的限制。