canvas绘图
HTML5添加的最受欢迎的元素,就是<canvas>元素,这个元素负责在页面中设置一个区域,然后可以通过javascript动态的在这个区域中绘制图形;
使用<canvas>元素,必须先设置其width和height属性,指定绘图区域大小;出现在canvas开头和结束标签之间的内容,会在不支持canvas元素的情况下显示;
写一个简单的canvas元素:
<canvas id="drawing" width="200" height="200">不支持canvas时,会显示canvas标签中的内容</canvas>
canvas元素对应的DOM元素对象也有width和height属性,可以随意修改,也可以通过css添加样式,如果不添加任何样式或不绘制任何图形,在页面中是看不到该元素的;
也就是,也可以通过css设置canvas元素的宽高属性:
<style> #drawing{ border:1px solid red; background:yellow; width:300px; height:300px; } </style> <body> <canvas id="drawing">不支持canvas时,会显示canvas标签中的内容</canvas> <script> </script> </body>
要在这块儿画布上绘图,需要取得绘图上下文。而取得绘图上下文的引用,需要调用getContext()方法并传入上下文的名字。传入“2d”,就可以获得2D上下文对象;
toDataURL()方法,可以导出在canvas元素上绘制的图像。这个方法接受一个参数,即图像的MIME类型格式,而且适合用于创建图像的任何上下文。比如要取得画布中的一副PNG格式的图像,可以使用一下代码:
<body> <canvas id="drawing" width="300" height="300">不支持canvas时,会显示canvas标签中的内容</canvas> <script> var drawing=document.getElementById("drawing"); //确定浏览器支持canvas元素 if(drawing.getContext){ var context=drawing.getContext("2d");//获取2D上下文 //取得图像的数据URI var imgURI=drawing.toDataURL("image/png"); //显示图像 var image=document.createElement("img"); image.src=imgURI; document.body.appendChild(image); } </script> </body>
默认情况下,浏览器会将图片编码为PNG格式(除非另行制定);
Firefox和Opera也支持基于“image/jpeg”参数的JPEG编码格式,如果绘制到画布上的图像源于不同的域,toDataURL()方法也会抛出错误,后续详细介绍;
2D上下文:
使用2D绘图上下文提供的方法,可以绘制简单的2D图形,比如矩形、弧线、和路径。2D上下文的坐标开始于canvas元素的左上角,原点坐标是(0,0)。所有坐标值都基于这个原点计算,x值越大表示越靠右,y值越大表示越靠下。默认情况下,width和height表示水平和垂直方向上可用的像素数目。
填充和描边:
2D上下文的两种基本绘图操作是填充和描边。填充就是用指定的样式(颜色、渐变或图像)填充图形;描边,就是只在图形的边缘画线。大多数2D上下文操作都会细分为填充和描边两个操作,而操作的结果取决于两个属性:fillStyle和strokeStyle。
这两个属性的值可以是字符串、渐变对象或模式对象,而且他们的默认值都是“#000000”。如果为他们指定表示颜色的字符串值,可以使用css中指定颜色值的任何格式,包括颜色名、十六进制码、rgb、rgba、hsl、hsla。举例:
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
context.strokeStyle = "red";
context.fillStyle = "#0000ff";
}
以上代码将strokeStyle设置为red(css中的颜色名),将fillStyle设置为#0000ff(蓝色)。然后,所有涉及填充和描边的的操作都将使用这两个样式,直至重新设置这两个值。如前所述,这两个属性的值也可以是渐变对象或模式对象。代码到这,在页面还看不出来任何效果,因为我们还没有正儿八经的开始绘制图形;只是设置了描边和填充属性;
绘制矩形:
与矩形有关的方法包括fillRect()、strokeRect()、clearRect()。这三个方法都接收4个参数:矩形的x坐标、矩形的y坐标、矩形的宽度和矩形的高度。单位都是像素。
首先,fillRect()方法在画布上绘制的矩形会填充指定的颜色。填充的颜色由fillStyle属性指定。
strokeRect()方法在画布上绘制的矩形会使用指定的颜色描边。描边颜色通过strokeStyle属性指定;
描边线条的宽度由lineWidth属性控制,该属性的值可以是任意整数。另外,通过lineCap属性可以控制线条末端的形状是平头、圆头还是方头(butt、round、square),通过lineJoin属性可以控制线条相交的方式是圆交、斜交、还是斜接(round、bevel、miter)。
<body> <canvas id="drawing" width="300" height="300" style="border:1px solid red;">不支持canvas时,会显示canvas标签中的内容</canvas> <script> var drawing=document.getElementById("drawing"); //确定浏览器支持canvas元素 if(drawing.getContext){ var context=drawing.getContext("2d");//获取2D上下文 //绘制红色矩形 context.fillStyle = "#ff0000";//填充颜色 context.fillRect(10,10,100,100);//填充命令 //描边 context.lineWidth="5";//线条宽度5像素 context.lineCap="square";//butt:平头、round:圆头、square:方头 context.lineJoin="round";//round:圆角、bevel:斜交、miter:斜接 context.strokeStyle="yellow";//描边颜色 context.strokeRect(10,10,100,100);//描边命令 //绘制半透明的蓝色矩形,后加的矩形会覆盖在前面的矩形上面 context.lineWidth="5";//线条宽度10像素 context.fillStyle = "rgba(0,0,255,0.5)";//填充颜色 context.fillRect(30,30,100,100);//矩形位置在(30,30)、矩形宽高为100x100 context.strokeStyle="blue";//描边颜色 context.strokeRect(30,30,100,100); //然后将画布绘制的图像导出,放在一个img标签中 var imgURI=drawing.toDataURL("image/png"); var image=document.createElement("img"); image.src=imgURI; image.style.cssText="border:1px solid red;display:block;" document.body.appendChild(image); } </script> </body>
最后clearRect()方法用于清除画布上的矩形区域。本质上,这个方法可以把绘制上下文的某一矩形区域变透明。通过绘制形状再清除指定区域,就可以清除有意思的效果,例如把某个形状切掉一块;例如:
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
//绘制红色矩形
context.fillStyle = "#ff0000";//填充颜色
context.fillRect(10,10,50,50);//填充命令
//绘制半透明的蓝色矩形
context.fillStyle = "rgba(0,0,255,0.5)";//填充颜色
context.fillRect(30,30,50,50);
//在两个矩形重叠的地方清除一个小矩形
context.clearRect(40,40,10,10);
}
绘制路径:
2D绘制上下文支持很多在画布上绘制路径的方法。通过路径可以创造出复杂的形状和线条。要绘制路径,首先必须调用beginPath()方法,表示要开始绘制新路径,然后再通过调用下列方法来实际的绘制路径:
arc(x,y,radius,startAngle,endAngle,counterclockwise):以(x,y)为圆心绘制一条弧线,弧线半径为radius,起始和结束角度(用弧度表示)为别表示为startAngle和endAngle。最后一个参数表示startAngle和endAngle是按逆时针方向计算,值为false表示按顺时针方向计算。
arcTo(x1,y1,x2,y2,radius):从上一点开始绘制一条弧线,到(x2,y2)为止,并且以给定的半径radius穿过(x1,y1)。
bezierCurveTo(c1x,c1y,c2x,c2y,x,y):从上一点开始绘制一条曲线,到(x,y)为止,并且以(c1x,c1y)和(c2x,c2y)为控制点。
lineTo(x,y):从上一点开始绘制一条直线,到(x,y)为止。
moveTo(x,y):将绘图游标移动到(x,y),不划线。
quadraticCurveTo(cx,cy,x,y):从上一点开始绘制一条二次曲线,到(x,y)为止,并且以(cx,cy)为控制点。
rect(x,y,width,height):从点(x,y)开始绘制一个矩形,宽度和高度分别为width和height指定。这个方法绘制的是矩形路径,而不是strokeRect()和fillRect()所绘制的独立的形状。
创建了路径之后,接下来有几种可能的选择。如果想绘制一条连接到路径起点的线条,可以调用closePath()。如果路径已经完成,你想用fillStyle填充它,可以调用fill()方法。另外,还可以调用stroke()方法对路径描边,描边使用的是strokeStyle。最后还可以调用clip(),这个方法可以在路径上创建一个剪切区域。
现在绘制一个不带数字的时钟表盘:
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
//开始绘制路径
context.beginPath();
//绘制外圈
context.arc(100,100,99,0,2*Math.PI,false);//以(100,100)为圆心,以99为半径,起始角度为0,结束角度为2π,顺时针画弧
//绘制内圆
context.moveTo(194,100);//移动一下起点
context.arc(100,100,94,0,2*Math.PI,false);
//绘制分针
context.moveTo(100,100);//游标移动到(100,100)
context.lineTo(100,15);//从上一点画直线到(100,15)
//绘制时针
context.moveTo(100,100);//游标移动到(100,100)
context.lineTo(35,100);//从上一点画直线到(35,100)
//描边路径
context.stroke();//绘制命令
}
在2D绘图上下文中,路径是一种主要的绘图方式,因为路径能为要绘制的图形提供更多控制。由于路径的使用很频繁,所以就有了一个名为isPointInPath()的方法。这个方法接收x和y坐标作为参数,用于在路径关闭之前确定画布上的某一点是否位于路径上,例如:
if(context.isPointInPath(100,100)){
console.log("这个点在路径上");
}else{
console.log("这个点不在路径上");
}
其它方法的一些实验:
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
//开始绘制路径
context.beginPath();
context.moveTo(100,100);
context.arcTo(150,50,200,100,50);//从上一点开始到(200,100)绘制一条弧线且经过(150,50)半径为50px
context.moveTo(110,100);
context.bezierCurveTo(130,50,160,50,210,100);//从上一点开始画一条贝塞尔曲线,终点为(210,100),以(130,50)和(160,50)为控制点
context.moveTo(100,110);
context.quadraticCurveTo(150,50,200,110);//从上一点开始绘制一条二次曲线,到(200,110)为止,并且以(150,50)为控制点
// context.moveTo(100,200);
context.rect(100,200,100,100);//从(100,200)开始绘制一个宽高都为100的矩形
//描边路径
context.stroke();//绘制命令
}
绘制文本:
绘制文本主要有两个方法:filltext()和strokeText()。这两个方法都可以接受4个参数:要绘制文本的字符窜,x坐标、y坐标、、可选的最大像素宽度。而且这两个方法都以下列三个属性为基础:
font:表示文本样式、大小及字体,用css中指定字体的格式来指定,例如“10px Arial”。
textAlign:表示文本对齐方式。可能值有“start”、“end”、“left”、“right”、“center”。建议使用“start”和“end”,不要使用“left”和“right”,因为前两者的意思更稳妥,能同时适合从左到右,从右到左的显示。
textBaseline:表示文本的基线。可能的值有:“top”、“hanging”、“middle”、“alphabetic”、“ideographic”、“bottom”。
这几个属性都有默认值,没必要每次使用都重新设置一遍。fillText()方法使用fillStyle属性绘制文本,而strokeText()方法使用strokeStyle属性为文本描边。相对来说还是使用fillText()的时候更多,因为该方法模仿了再网页中正常的显示文本。
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
//绘制文本
context.fillStyle="red";//可选
context.font="bold 14px Arial";
context.textAlign="center";
context.textBaseline="middle";
context.fillText("哈哈哈",100,20);
}
因为这里把textAlign设置为center,把textBaseline设置为“middle”,所以坐标(100,20)表示的是文本水平和垂直中点的坐标。如果将textAlign设置为start,则x坐标表示的是文本左端的位置(从左到右阅读语言);设置为“end”,则x坐标表示的是文本右端的位置(从左到右阅读的语言)。
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
//绘制文本
context.fillStyle="red";//可选
context.font="bold 14px Arial";
context.textAlign="center";
context.textBaseline="middle";
context.fillText("哈哈哈",100,20);
//起点对齐
context.textAlign="start";
context.fillText("哈哈哈",100,40);
//终点对齐
context.textAlign="end";
context.fillText("哈哈哈",100,60);
}
2D上下文提供了辅助确定文本大小的方法measureText()。这个方法接收一个参数,即要绘制的文本;返回一个TextMetrics对象。返回的对象目前只有一个width属性。
measureText()方法利用font、textAlign和textBaseline的当前值计算指定文本的大小。
比如想在一个140像素宽的矩形区域中绘制文本Hello world!,下面的代码从100像素的字体大小开始递减,最终找到合适的字体大小。
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
//绘制文本
var fontSize=100;
context.font=fontSize+"px Arial";
while(context.measureText("Hello world!").width>140){
fontSize--;
context.font=fontSize+"px Arial";
}
context.fillText("Hellow world!",10,20);
context.fillText("字体大小为"+fontSize+"px",10,60);
}
变换:通过上下文的变换,可以把处理后的图像绘制到画布上。2D绘制上下文支持各种基本的绘制变换。为绘制上下文应用变换,会导致使用不同的变换矩阵应用处理,从而产生不同的结果。
可以通过如下方法来修改变换矩阵:
rotate(angle):围绕原点旋转图像angle弧度。
scale(scalex,scaley):缩放图像,在x方向乘以scalex,在y方向乘以scaley,scalex和scaley的默认值都为1.0
translate(x,y):将坐标原点移动到(x,y)。执行这个变换后,坐标(0,0)会变成之前由(x,y)表示的点;
transform(m1—1,m1—2,m2—1,m2—2,dx,dy):直接修改变换矩阵,方式是乘以如下矩阵:
m1—1 | m1—2 | dx |
m2—1 | m2—2 | dy |
0 | 0 | 1 |
setTransform(m1—1,m1—2,m2—1,m2—2,dx,dy):将变换矩阵重置为默认状态,然后再调用transform()。
变换有可能简单,也有可能复杂,拿前面的绘制表针来说,如果把原点变换到表盘的中心,然后再绘制表针就容易多了。
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
//开始路径
context.beginPath();
// 绘制外圆
context.arc(100,100,99,0,2*Math.PI,false);//绘制一个以(100,100)为圆心,99位半径,开始角度为0,结束角度为2π,顺时针绘制的弧线(圆)
// 绘制内圆
context.moveTo(194,100);
context.arc(100,100,94,0,2*Math.PI,false);
// 变换原点
context.translate(100,100);//将原点移到(100,100)此时该坐标的坐标变为(0,0)
// 绘制分针
context.moveTo(0,0);
context.lineTo(0,-85);
// 绘制时针
context.moveTo(0,0);
context.lineTo(-65,0);
// 描边
context.stroke();
}
还可以更近一步,用rotate()方法旋转时钟的表针;
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
//开始路径
context.beginPath();
// 绘制外圆
context.arc(100,100,99,0,2*Math.PI,false);//绘制一个以(100,100)为圆心,99位半径,开始角度为0,结束角度为2π,顺时针绘制的弧线(圆)
// 绘制内圆
context.moveTo(194,100);
context.arc(100,100,94,0,2*Math.PI,false);
// 变换原点
context.translate(100,100);//将原点移到(100,100)此时该坐标的坐标变为(0,0)
// 旋转表针
context.rotate(1);//3.14表示1π,也就是180度(按原点旋转,顺时针)
// 绘制分针
context.moveTo(0,0);
context.lineTo(0,-85);
// 绘制时针
context.moveTo(0,0);
context.lineTo(-65,0);
// 描边
context.stroke();
}
无论是刚才执行的变换,还是fillStyle,strokeStyle等属性,都会在当前上下文中一直有效,除非再对上下文进行什么修改,虽然没办法把上下文的一切重置回默认值,但有两个方法可以跟踪上下文的变化状态。如果你知道将来还要返回某组属性与变换的组合,可以调用save()方法。调用这个方法后,当时的所有设置都会进入一个栈结构,得以妥善保管。然后可以对上下文进行其他修改。等想要回到之前保存的设置时,可以调用restore()方法,在保存设置的栈结构中向前返回一级,恢复之前的状态。连续调用save()可以把更多设置保存到栈结构中,之后再连续调用restore()则可以一级一级返回。下面来看一个例子:
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
context.fillStyle="#ff0000";
context.save();//保存状态
context.fillStyle="#00ff00";
context.translate(100,100);
context.save();
context.fillStyle="#0000ff";
context.fillRect(0,0,100,200);//从(100,100)开始绘制蓝色矩形
context.restore();//状态返回一级
context.fillRect(10,10,100,200);//从(110,110)开始绘制绿色矩形
context.restore();//再返回一级
context.fillRect(0,0,100,200);//从点(0,0)开始绘制红色矩形
}
save()方法只是保存对绘图上下文的设置和变换,不会保存绘图上下文的内容;
绘制图像:
2D绘图上下文内置了对图像的支持,如果你想把衣服图像绘制在画布上,可以使用drawImage()方法。根据期望的最终结果不同,调用这个方法时,可以使用三个不同的参数组合。最简单的调用方式是传入一个HTML<img>元素,以及绘制该图像的起点的x和y坐标。例如:
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
var image=document.images[0];
window.onload=function(){//加上load方法,是为了等图片加载完再往画布上绘制
// context.drawImage(image,10,10);
context.drawImage(image,10,10,200,200);//后面两个参数是指的图像的宽度和高度(px)
}
}
除了上述两种方式,还可以选择把图像中的某个区域绘制到上下文中。drawImage()方法的这种调用方式总共需要传入9个参数:要绘制的参数、源图像的x坐标、源图像的y坐标、原图像的宽度、原图像的高度、目标图像的x坐标、目标图像的y坐标、目标图像的宽度、目标图像的高度。这样调用drawImage()方法可以获得更多控制。例如:
源图像代表原来图像的尺寸,从(0,0)开始圈一块儿地方;再将目标图像绘制在画布的某个位置,(规定这块图像的大小);
也就是第一步就是在原图上指定位置圈一块儿,放在画布的某个位置上,并指定大小
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
var image=document.images[0];
window.onload=function(){
//在原图像的(300,0)位置上圈300x300的一块儿图像,绘制在画布上(0,0)的位置,大小为100x100
context.drawImage(image,300,0,300,300,0,0,100,100);
}
}
谨记,不能用外域的图像。当然除了把img标签绘制在画布上外,也可以将canvas标签绘制在画布上;
阴影:
2D上下文会根据以下几个属性的值,自动为形状或路径绘制出阴影。
shadowColor:用css颜色的格式表示额阴影颜色,默认为黑色。
shadowOffsetX:形状或路径x轴方向的阴影偏移量,默认0;
shadowOffsetY:形状或路径y轴方向的阴影偏移量,默认为0;
shadowBlur:模糊的像素数,默认0,即不模糊;
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
//设置阴影
context.shadowOffsetX=5;
context.shadowOffsetY=4;
context.shadowBlur=4;
context.shadowColor="rgba(0,0,0,.5)";
// 绘制红色矩形
context.fillStyle="#ff0000";
context.fillRect(10,10,50,50);
//绘制蓝色矩形
context.fillStyle="rgba(0,0,255,1)";
context.fillRect(30,30,50,50);
}
chrome(10-)、safari(5-)浏览器较低版本在给带透明像素的图像应用阴影时,隐隐有可能出不来;
渐变:
渐变由CanvasGradient实例表示,很容易通过2D上下文来创建和修改。要创建一个新的线性渐变,可以调用createLinearGradient()方法。这个方法接收4个参数:起点x坐标,起点y坐标,终点x坐标,终点y坐标。调用这个方法后,它就创建了一个指定大小的渐变,并返回CanvasGradient对象的实例;
创建了渐变对象后,下一步就是使用addColorStop()方法来指定色标。这个方法接收两个参数:色标位置和css颜色值。色标位置是一个0(开始的颜色)到1(结束颜色)之间的数字。例如:
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
// 创建一个CanvasGradient对象
var gradient=context.createLinearGradient(30,30,70,70);
gradient.addColorStop(0,"white");
gradient.addColorStop(1,"black");
//此时gradient对象表示的是一个从画布上点(30,30)到点(70,70)的渐变。起点的色标是白色终点的色标是黑色。然后就可以把fillStyle或strokeStyle设置为这个对象,从而用渐变来绘制形状或描边:
// 绘制红色矩形
context.fillStyle="#ff0000";
context.fillRect(10,10,50,50);
// 绘制渐变色矩形
context.fillStyle=gradient;
context.fillRect(30,30,50,50);
}
为了让渐变色覆盖整个矩形,而不是仅应用到矩形的一部分,矩形和渐变对象的坐标必须匹配才行,如果没将矩形绘制到指定位置,那可能就会显示部分渐变效果:
//若将矩形没绘制到与阴影坐标匹配的位置,则可能显示部分渐变效果
context.strokeStyle="yellow";
context.lineWidth=1;
context.strokeRect(20,50,50,50);//描边命令
context.fillStyle=gradient;
context.fillRect(20,50,50,50);
确保渐变与形状对齐非常重要,有会后可以考虑使用函数来确保坐标合适。例如
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
// 用确保渐变与形状对齐的方法创建一个gradient对象
var gradient=createRectLinearGradient(context,30,30,50,50);
gradient.addColorStop(0,"white");
gradient.addColorStop(1,"black");
// 绘制渐变矩形
context.fillStyle=gradient;
context.fillRect(30,30,50,50);//这样,渐变对象坐标和矩形坐标相同就可以保证两者匹配
}
// 确保渐变与形状对齐的方法
function createRectLinearGradient(context,x,y,width,height){
return context.createLinearGradient(x,y,x+width,y+height);
}
以上是线性渐变,下面说一下径向渐变,可以使用createRadialGradient()方法,这个方法接收6个参数,对应两个圆的圆心和半径。前3个参数是起点圆的圆心(x和y)及半径,后3个参数指的是终点圆的圆心(x和y)及半径。可以把径向渐变想象成一个长圆桶,而这6个参数定义的是这个桶的两个圆形开口位置,如果把一个原圆形开口定义的比另一个小一些,那这个圆桶就变成了圆锥体,而通过移动每个圆形开口的位置,就可以达到下个旋转一个圆锥体一样的效果。
如果想从某个形状的中心点开始创建一个向外扩撒的径向渐变效果,就要将两个圆定义为同心圆。
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
// 创建径向渐变对象
var gradient=context.createRadialGradient(55,55,10,55,55,30);
gradient.addColorStop(0,"white");
gradient.addColorStop(1,"black");
//绘制红色矩形
context.fillStyle="red";
context.fillRect(10,10,50,50);
// 绘制径向渐变矩形
context.fillStyle=gradient;
context.fillRect(30,30,50,50);
}
模式:
模式其实就是重复的图像,可以用来填充或描边。要创建一个新模式,可以调用createPattern()方法并传入两个参数:一个HTML<img>元素和一个表示如何重复图像的字符串。其中,第二个参数的值与css的background-repeat属性值相同,包括"repeat"、“repeat-x”、“repeat-y”和“no-repeat”。例:
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
var image=document.images[0];
window.onload=function(){//等图像加载好再创建模式
// 创建一个模式
var pattern=context.createPattern(image,"repeat");
// 绘制矩形
context.fillStyle=pattern;
context.fillRect(10,10,200,200);
}
}
需要注意的是,模式是重原点(0,0)开始的。将填充样式(fillStyle)设置为模式对象,只表示在某个特定的区域内显示重复图像,而不是要从某个位置开始绘制重复图像
。另外ctreatePattern()方法的第一个参数也可以是一个<video>元素,或者另一个<canvas>元素。
使用图像数据:
2D上下文的一个明显的长处就是,可以通过getImageData()取得原始图像数据。这个方法接收4个参数:要取得其数据的画面区域的x和y坐标以及该区域的像素宽度和高度。例如,要取得左上角坐标为(10,5)、大小为50x50像素的区域的图像数据,可以使用以下代码;
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");//获取2D上下文
var image=document.images[0];
var imageData=context.getImageData(10,5,30,30);
console.log(imageData)
}
这里返回的对象是ImageData的实例。每个ImageData对象都 有三个属性:width、height和data。其中data属性是一个数组,保存着图像中每一个像素的数据。在data数组中,每一个像素用4个元素来来保存,分别表示红、绿、蓝和透明度值。因此,第一个像素的数据就保存在数组的第0到第3个元素中。例如通过修改图像数据,可以像下面这样创建一个简单的灰阶过滤器。
var drawing=document.getElementById("drawing"); //确定浏览器支持canvas元素 if(drawing.getContext){ var context=drawing.getContext("2d"), image=document.images[0], imageData,data, i,len,average, red,green,blue,alpha; window.onload=function(){//保证图片加载完 // 绘制原始图像 context.drawImage(image,0,0); console.log(image) } // 取得图像数据 imageData=context.getImageData(0,0,image.width,image.height); data=imageData.data; console.log(imageData); for(i=0,len=data.length;i<len;i+=4){ red=data[i]; green=data[i+1]; blue=[i+2]; alpha=data[i+3]; //求得rgb的平均值 average=Math.floor((red+green+blue)/3); //设置颜色值,透明度不变 data[i]=average; data[i+1]=average; data[i+2]=average; } //回写图像数据并显示结果 imageData.data=data; context.putImageData(imageData,0,0); }
合成:
globalAlpha:透明属性,介于0,-1之间,包括0,也包括1
globalCompositionOperation:表示后绘制的图形怎么与先绘制的图形结合,可能值如下:
source-over(默认值):后绘制的图形位于先绘制的图形上方;
source-in:后绘制的图像与先绘制的图形重叠的部分可见,两者其它部分完全透明;
source-out:后绘制的图形与先绘制的图形不重叠的部分可见,先绘制的图形完全透明;
source-atop:后绘制的图形与先绘制的图形重叠的部分可见,先绘制的图形不受影响;
destination-over:后绘制的图形位于先绘制的图形下方,只有之前透明像素下的部分才可见;
destination-in:后绘制的图形位于先绘制的图形下方,两者不重叠的部分完全透明。
destination-out:后绘制的图形擦除与先绘制的图形重叠的部分。
destination-atop:后绘制的图形位于先绘制的图形下方,在两者不重叠的地方,先绘制的图形会变透明。
lighter:后绘制的图形与先绘制的图形重叠部分的值相加,使该部分变亮。
copy:后绘制的图形完全替代与之重叠的先绘制图形。
xor:后绘制的图形与先绘制的图形重叠的部分执行“异或”操作;
var drawing=document.getElementById("drawing");
//确定浏览器支持canvas元素
if(drawing.getContext){
var context=drawing.getContext("2d");
//绘制红色矩形
context.fillStyle="#ff0000";
context.fillRect(10,10,50,50);
// 修改全局透明度
context.globalAlpha=0.5;
//绘制蓝色矩形
context.fillStyle="rgba(0,0,255,1)";
context.fillRect(30,30,50,50);
//重置全局透明度
context.globalAlpha=1;
//绘制红色矩形
context.fillStyle="#ff0000";
context.fillRect(100,100,50,50);
//设置合成操作
context.globalCompositeOperation="destination-over";
//绘制蓝色矩形
context.fillStyle="rgba(0,0,255,1)";
context.fillRect(120,120,50,50);
}
WebGL:
WebGL是针对Canvas的3D上下文。WebGL并不是W3C指定标准,而是Khronos Group制定的。浏览器中使用的WebGL就是基于OpenGL ES2.0制定的。
全面了解OpenGL,访问www.opengl.org;全面学习WebGL,参考www.learningwebgl.com
类型化数组:
WebGl涉及的复杂计算需要提前知道数值的精度,而标准的javascript数值无法满足需求。为此,WebGL引入了一个概念,叫类型化数组(type arrays)。类型化数组也是数组,只不过其元素被设置为特定的值;
类型化数组的核心就是一个名为ArrayBuffer的类型。没个ArrayBuffer对象表示的只是内存中指定的字节数,但不会指定这些字节用于保存什么类型的数据。通过ArrayBuffer所能做的,就是为了将来使用而分配一定数量的字节。例如,下面这行代码会在内存中分配20B。
var buffer = new ArrayBuffer(20);
console.log(buffer);//ArrayBuffer { byteLength: 20 }
//访问其byteLength属性
console.log(buffer.byteLength);//20
虽然ArrayBuffer对象本身没有什么可说的,但对WebGL而言,使用它时及其重要的;
视图:
使用ArrayBuffer(数组缓冲器类型)的一种特别的方式就是用它来创建数组缓冲器视图。其中最常见的视图是DataView,通过它可以选择ArrayBuffer中一小段字节。为此,创建DataView实例的时候传入一个ArrayBuffer、一个可选字节偏移量(从该字节开始选择)和一个可选的要选择的直接数;
var buffer = new ArrayBuffer(20);
console.log(buffer);//ArrayBuffer { byteLength: 20 }
//访问其byteLength属性
console.log(buffer.byteLength);//20
// 基于整个缓冲器创建一个新视图
var view=new DataView(buffer);
console.log(view);
// 创建一个开始于字节9的新视图
var view=new DataView(buffer,9);
console.log(view);
// 创建一个从字节9开始到字节18的新视图
var view =new DataView(buffer,9,10);
console.log(view);
实例化之后,DataView对象会把字节偏移量以及字节长度信息分别保存在byteOffset和byteLength属性中。
读取和写入DataView的时候,要根据实际数据的类型,选择相应的getter和setter方法。
数据类型 | getter | setter |
有符号8位整数 | getInt8(byteOffset) | setInt8(byteOffset,value) |
无符号8位整数 | getUint8(byteOffset) | setUint8(byteOffset,value) |
有符号16位整数 | getInt16(byteOffset,littleEndian) | setInt16(byteOffset,value,littleEndian) |
无符号16位整数 | getUint16(byteOffset,littleEndian) | setUint16(byteOffset,value,littleEndian) |
有符号32位整数 | getInt32(byteOffset,littleEndian) | setInt32(byteOffset,value,littleEndian) |
无符号32位整数 | getUint32(byteOffset,littleEndian) | setUint32(byteOffset,value,littleEndian) |
32位浮点数 | getFloat32(byteOffset,littleEndian) | setFloat32(byteOffset,value,littleEndian) |
64位浮点数 | getFloat64(byteOffset,littleEndian) |
setFloat64(byteOffset,value,littleEndian) |
有些数据类型可能需要不止1b。比如无符号8位整数要用1B,而32位浮点数则要用4B。
var buffer = new ArrayBuffer(20),
view=new DataView(buffer),
value;
view.setUint16(0,25);
view.setUint16(2,50);//不能从字节1开始,因为16位整数要用2B
value=view.getUint16(0);
console.log(value);//25
用于读写16位或更大数值的方法都有一个可选参数littleEndian。这是一个布尔值;表示读写数值时是否采用小端字节序(将数据的最低有效位保存在低内存地址中),而不是大端字节序(将数据最低有效位保存在高内存地址中)。不传,就是默认大端字节序;
var buffer = new ArrayBuffer(20),
view=new DataView(buffer),
value;
view.setUint16(0,25);
view.setUint16(2,50);//不能从字节1开始,因为16位整数要用2B
value=view.getUint8(0);
console.log(value);//0
类型化视图:
类型化视图一般也称为类型化数组,因为他们除了元素必须是某种特定的数据类型外,与常规的数组无异;
Init8Array | 表示8位二补整数 |
Uint8Array | 表示8位无符号整数 |
Init16Array | 表示16位二补整数 |
Uint16Array | 表示16位无符号整数 |
Init32Array | 表示32位二补整数 |
Uint32Array | 表示32位无符号整数 |
Float32Array | 表示32位IEEE浮点数 |
Float64Array | 表示64位IEEE浮点数 |
var buffer = new ArrayBuffer(20);
//创建一个新数组,使用整个缓冲器
var int8s=new Int8Array(buffer);
// 只使用字节9开始的缓冲器
var int16s=new Int16Array(buffer,9);
//只使用从字节9到字节18的缓冲器
var uint16s=new Uint16Array(buffer,9,10);
能够指定缓冲器中可用的字节段,意味着能在同一个缓冲器中保存不同类型的数值;
var buffer = new ArrayBuffer(20);
// 使用缓冲器的一部分保存8位整数,另一部分保存16位整数。
var int8s=new Int8Array(buffer,0,10);
console.log(int8s);
var unit16s=new Uint16Array(buffer,10,10);
console.log(unit16s);
// 每个视图的构造函数都有一个名为BYTES_PER_ELEMENT的属性,表示类型化数组的每个元素需要多少字节。
//需要10个元素空间
var ints=new Int8Array(buffer,0,10*Int8Array.BYTES_PER_ELEMENT);
// 需要5个元素空间
var unit16s=new Uint16Array(buffer,int8s.byteOffset+ints.byteLength,5*Uint16Array.BYTES_PER_ELEMENT);
console.log(ints);
console.log(unit16s);
另外,创建类型化视图还可以不用首先创建ArrayBuffer对象。只要传入希望数组保存的元素数,相应的构造函数就可以自动创建一个包含足够字节数的ArrayBuffer对象,例如:
//创建一个数组保存10个8位整数(10字节)
var int8s=new Int8Array(10);
//创建一个数组保存10个16位整数(20字节)
var unit16s=new Uint16Array(10);
// 也可以把常规数组转化为类型化数组
// 创建一个数组保存5个8位整数(10字节)
var ints=new Int8Array([10,20,30,40,50]);
//类型化视图还有一个方法subarray(),可以基于底层数组缓冲器的子集创建一个新视图
var uint16s=new Uint16Array(10),//创建一个保存10个16位整数的类型化视图
sub=uint16s.subarray(2,5);//偏移量为2,长度为5
不行,不行,WebGL看的有些头蒙,再加上目前支持的浏览器只有火狐和谷歌,而且默认还是禁用的,我找了个很好的理由,暂时停止学习WebGL,有时间再收拾它。。。