这是本系列的最后一篇入门文章,主要是对剩余的未说明的canvas方法来逐个介绍。
接着上面...
首先,如果你是一名擅长矢量设计的设计师,对Illustrator或者Fireworks很熟悉的话,那你肯定知道它们有一个很强大的矢量混合处理功能,可以对多个矢量路径进行“合并”、“拆分”、“结合”、“相交”等系列操作。 如下图,在Fireworks中有三个矢量路径,我先把灰色的圆形和粉红色的矩形进行了“结合路径”的处理,接着又把结合后的路径跟绿色的星形矢量进行了“合并路径”操作:
一、图形组合
当我们在canvas中绘制的图形重合在一起的时候,能看到哪个图形则完全取决于图形的绘制顺序(后绘制的图形会覆盖掉之前绘制的图形,当然,如果后面绘制的图形颜色透明度不为1,则会受之前绘制图形的颜色影响).
在canvas中也有实现同样功能的属性—— globalCompositeOperation,利用它,可以实现对画布上已有的图像(暂称为“目标图像”)和新绘制的图像(暂成为“源图像”)来执行相关的混合处理。
图形上下文对象的globalCompositeOperation属性能让我们自己决定图形的组合方式,如context.globalCompositeOperation = type;
globalCompositeOperation的可取值见下表:
其中type的值必须是下面几种字符串之一:
值 | 描述 |
---|---|
source-over | 默认。在目标图像上显示源图像。 |
source-atop | 在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。 |
source-in | 在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。 |
source-out | 在目标图像之外显示源图像。只会显示目标图像之外源图像部分,目标图像是透明的。 |
destination-over | 在源图像上方显示目标图像。 |
destination-atop | 在源图像顶部显示目标图像。源图像之外的目标图像部分不会被显示。 |
destination-in | 在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的。 |
destination-out | 在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。 |
lighter | 显示源图像 目标图像。 |
copy | 显示源图像。忽略目标图像。 |
xor | 使用异或操作,对源图像与目标图像进行结合处理。 |
type值 | type所代表的的含义 |
source-over(默认值) | 为默认值,表示新图形覆盖在原有图形之上 |
destination-over | 在原有图形之下绘制新图形 |
source-in | 只显示新图形中与原有图形相重叠的部分,其他部分则变成透明 |
destination-in | 只显示原有图形中与新图形相重叠的部分,新图形与原有图形的其他部分都透明 |
source-out | 只显示新图形中与原有图形不重叠的部分,新图形与原有图形的其他部分变成透明 |
destination-out | 只显示原有图形中与新图形不重叠的部分,新图形与原有图形的其他部分变成透明 |
source-atop | 只绘制新图形中与原有图形重叠的部分与未被重叠覆盖的原有图形,新图形的其他部分变成透明 |
destination-atop | 只绘制原有图形中被新图形覆盖的部分与新图形的其他部分,原有图形的其他部分变成透明 |
lighter | 原有图形与新图形均绘制,重叠部分做加色处理 |
xor | 只绘制新图形中与原有图形不重叠的部分,重叠部分变成透明 |
copy | 只绘制新图形,原有图形中未与新图形重叠的部分变成透明 |
我们绘制一个蓝色的圆形和一个粉红色的矩形,对它们进行类似上图“结合路径”(xor)的处理:
<canvas id="myCanvas" width="300" height="200" style="border:solid 1px #CCC;"> 您的浏览器不支持canvas,建议使用最新版的Chrome </canvas> <script type="text/javascript"> var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.arc( 80, 80, 40, 0, 2*Math.PI); ctx.fillStyle = "#4DA6FF"; ctx.fill(); ctx.fillStyle = "#FF7373"; ctx.globalCompositeOperation="xor"; //异或结合处理 ctx.fillRect(95,50,100,90); </script>
效果如下:
各globalCompositeOperation 属性值的最终效果可以参考下图(蓝色矩形为目标图像,红色圆形为后续新绘制的源图像):
接着介绍 isPointInPath()
方法,它接受一个指定点的坐标值作为参数,可判断该指定点是否位于当前路径内,并返回对应的Boolean值。
我们来绘制一个矩形,然后判断点(115,
70)是否位于该矩形路径内部,如果是,则填充该矩形,否则alert出相关信息:
<script type="text/javascript"> var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.rect(95,50,100,90); if (ctx.isPointInPath(115,70)){ //判断点(115,70)是否位于当前路径内 ctx.fillStyle = "#FF7373"; ctx.fill(); }else{ alert("不在矩形路径内"); } </script>
由于该点是位于矩形路径上的,故成功绘制矩形,而不会弹窗:
我们常规使用canvas绘制多个图片的时候,总有一些会重复使用到的状态,比如我们先绘制了一个“倾斜20度、填充颜色为红色”的矩形,接着绘制了一个“不倾斜、填充颜色为蓝色”的矩形。如果后面我们又要重复绘制一个“倾斜20度、填充颜色为红色”的矩形,又得重新设置画布的setTransform和fillStyle值,自然是挺烦人的事情。
canvas的 save() 和 restore() 方法便能为我们处理这个问题——使用
.save() 方法能为我们保存当前画布的状态,使用 .restore()
方法可以把当前画布状态返回到上一次保存的状态,有点类似于我们玩游戏过程中存档和读档的功能。
我们先说说canvas的“状态”,它指的是像我们上面提到的“倾斜(变形)、填充”这样的配置,是当前所有样式和变形的一个快照。我们可以保存和读取到的状态包括:
当前的 transformation matrix;
当前的 clipping region;
当前的属性值:fillStyle, font, globalAlpha,
globalCompositeOperation, lineCap, lineJoin,
lineWidth, miterLimit, shadowBlur, shadowColor,
shadowOffsetX, shadowOffsetY, strokeStyle, textAlign,
textBaseline
canvas的状态是以栈(stack)的方式保存的,遵循先进后出(FILO)原则。每一次调用
save 方法,当前的状态就会被推入栈中保存起来;当调用 restore
方法时,最新入栈的状态则被推出使用。
我们来一个简单的例子:
<canvas id="myCanvas" width="150" height="150" style="border:solid 1px #CCC;"> 您的浏览器不支持canvas,建议使用最新版的Chrome </canvas> <script type="text/javascript"> var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.fillStyle = "green"; ctx.fillRect(20,20,100,100); ctx.save(); //保存状态(fillStyle = "green") ctx.fillStyle = "red"; ctx.fillRect(40,40,60,60); ctx.restore(); //读取上次保存的状态(fillStyle = "green") ctx.fillRect(60,60,20,20); //填充了绿色的矩形而非红色的 </script>
可在绘制路径之前,提前设置context.globalCompositeOperation = [[type]]-->11中类型,如果,未填写正确类型,则会是默认类型
效果如下:
二、给图形绘制阴影
上面的例子很好理解,不过为了了解“状态入栈出栈”的情况,我们把上面例子改造一下:
使用图形上下文对象的几个关于阴影绘制的属性即可添加阴影效果
<canvas id="myCanvas" width="150" height="150" style="border:solid 1px #CCC;"> 您的浏览器不支持canvas,建议使用最新版的Chrome </canvas> <script type="text/javascript"> var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.fillStyle = "yellow"; //注意这里多了一行状态定义 ctx.save(); //注意这里多了一行状态保存 ctx.fillStyle = "green"; ctx.fillRect(20,20,100,100); ctx.save(); ctx.fillStyle = "red"; ctx.fillRect(40,40,60,60); ctx.restore(); //推出最后一个入栈的状态(fillStyle = "green") ctx.restore(); //再次推出最后一个入栈的状态(fillStyle = "yellow"),注意green已经在上次restore推出了,故这次取到的是yellow ctx.fillRect(60,60,20,20); //填充黄色的矩形 </script>
shadowOffsetX-->阴影的横向偏移量
就像注释说明的那样,最终绘制的小矩形是黄色而非绿色:
shadowOffsetY-->阴影的纵向偏移量
shadowColor-->阴影的颜色
最后介绍的是 toDataURL() 方法,它能把当前画布的内容转换为data类型的URL格式。 等等,“data类型的URL格式”又是什么来的? 有时候我们会在某些页面(比如github404页面)看到这样的图片:
shadowBlur-->阴影的模糊范围,属性可选,它表示阴影边缘的模糊范围,如果不希望阴影的边缘太清晰,需要将阴影的边缘模糊化时可以使用该属性,设定的属性值为比0大的数字,否则会被忽略,一般设定0到10之间
注:使用图形上下文对象的绘制阴影属性,这几个属性与路径无关,只要设定一次,全部的图形都会具有阴影效果,如果想让某一个图形没有阴影效果,只需要重新将shadowColor设定为rgba(0,0,0,0)就可以了.
其src对应的是一串“data:image/png;base64....”的data类型URL格式,返回的base64图片数据流,可以减少对服务器的图片请求(当然缺点是无法缓存在客户端)。这种格式这就是我们上面提到的东西。 那么 toDataURL() 方法的好处就显而易见了——我们可以把当前画布内容转换为一张图片。
function draw (obj) {
var $myCanvas = document.getElementById(obj);
if ($myCanvas == null) return false;
var $ctx = $myCanvas.getContext('2d');
$ctx.fillStyle = '#eef';
$ctx.fillRect(0,0,400,300);
$ctx.shadowOffsetX = 10;
$ctx.shadowOffsetY = 10;
$ctx.shadowColor = 'rgba(100,100,100,0.5)';
$ctx.shadowBlur = 7.5;
$ctx.translate(200,50);
for (var i = 0; i < 50; i ) {
$ctx.translate(25,25);
$ctx.scale(0.95,0.95);
$ctx.rotate(Math.PI/10);
create5Star($ctx);
$ctx.fill();
}
}
function create5Star (context) {
var n = 0,
dx = 100,
dy = 0,
s = 50;
context.beginPath();
context.fillStyle = 'rgba(255,0,0,0.5)';
var x = Math.sin(0),
y = Math.cos(0),
dig = Math.PI/5*4;
for (var i = 0; i < 5; i ) {
var x = Math.sin(i*dig),
y = Math.cos(i*dig);
context.lineTo(dx x*s,dy y*s);
}
context.closePath();
}
toDataURL() 方法直接作用于canvas对象,而非context2D对象,其格式为:
三、使用图像
url = canvas.toDataURL( [ type, ... ] )
接下来是我个人认为比较重要的几个用途.
参数type表示返回文件的MINE类型,若未指定type,则默认返回的格式必须为PNG格式。如果canvas没有任何像素,则返回值为:“data:,”,这是最短的data:URL,在text/plain资源中表现为空字符串。
type的值可以在image/png,image/jpeg,image/svg xml
等MIME类型中选择。如果是image/jpeg,可以有第二个参数,如果第二个参数的值在0-1之间,则表示JPEG的质量等级,否则使用浏览器内置默认质量等级。
1.绘制图像
我们来个小例子:
在canvas中可以读取图像文件,然后将之绘制在画布当中,绘制图像的方法:drawImage
<canvas id="myCanvas" width="150" height="150" style="border:solid 1px #CCC;"></canvas> <button onClick="openImg()">点我查看图片</button> <script type="text/javascript"> var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.fillStyle = "green"; ctx.fillRect(20,20,100,100); ctx.save(); ctx.fillStyle = "red"; ctx.fillRect(40,40,60,60); ctx.restore(); ctx.fillRect(60,60,20,20); function openImg(){ var image = c.toDataURL("image/png"), //注意toDataURL是canvas对象方法,而不是context2D的属性 w = window.open('about:blank'); w.document.write("<img src='" image "' />"); } </script>
context.drawImage(image,x,y); ===> x,y-->绘制时该图像在画布中的起始坐标
点击“点我查看图片”的按钮后会弹出一个新页面,上面有我们生成的base64编码形式的图片(跟canvas上绘制的内容一致):
context.drawImage(image,x,y,w,h); ===> 前三个参数与上面一样,w、h则是指绘制时的图像(画布区域出现的图像的宽高)的宽度与高度,第一种方法中省略了这两个参数,所以绘制出来的图像与原图大小相同,
context.drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh); ===> 第三种方法则是将画布中已经绘制好的图像的全部或则局部区域复制到画布中的另一个位置上,除了image仍然代表被复制的图像文件,前面四个参数是原图像的被复制区域,后面四个参数是画布中绘制出图像的区域.
但由于该图片是不存在于任何文件目录上的,自然也无法直接下载该(带后缀名的)文件,如果想要地道地生成一张图片供用户下载,则需要后端配合了(可以参考这里)。
另外有一点要注意的,也就是我们在第五章提到的,如果在canvas中绘制了非本地的图片,从安全性考虑将无法对其进行读操作,这种情况下如果执行toDataURL自然也会报错。不过如果资源开启了CORS,则有办法避开同源策略的阻拦(可以参考这里)。
上面三种形式中,image都代表一个图片对象,用该对象来装载图片文件.
绘制图像时需要使用不带参数的new方法创建Image对象,然后设定该Image对象的src属性为需要绘制的图像文件的路径,如下
自此,canvas的api我们基本都遍历了一遍,希望能让来此学习的朋友有所收获,共勉~
var image = new Image();
image.src = 'image1.jpg'; //设置图像路径
然后就可以使用drawImage方法绘制该图像文件了
事实上,即时设定好Image对象的src属性后,也不一定立刻就能把图形绘制完毕,譬如,有时该图像文件是一个来源于网络的比较大的图像文件,这时候用户就得耐心等待图像全部加载完毕才能看见该图像了.
这种情况下,可以使用
image.onload = function(){} //绘制图像的函数
在Image对象的onload事件中同步执行绘制图像的函数,就可以一边装载一边绘制了.
function draw (obj) {
var $myCanvas = document.getElementById(obj);
if ($myCanvas == null) return false;
var $ctx = $myCanvas.getContext('2d');
var img = new Image();
img.src = 'http://img17.3lian.com/d/file/201702/22/1005a2e0825ffe290b3f697404ee8038.jpg';
img.onload = function () {
$ctx.drawImage(img,0,0,400,300);
}
}
2.图像平铺
使用图像上下文对象的createPattern方法就可以实现图像平铺效果: context.createPattern(image,type);
其中image为要平铺的对象,type的值则有下面几种类型(注意,type值为字符串类型)
本文由wns9778.com发布于计算机教程,转载请注明出处:canvas学习之路--记录(二)
关键词: wns9778.com